异步与多线程-它们有什么区别吗?

146

异步调用是否总是会创建一个新的线程?这两者有什么区别?

异步调用是否总是会创建或使用一个新的线程?

维基百科上说:

在计算机编程中,异步事件是指独立于主程序流程发生的事件。异步操作是以非阻塞方式执行的操作,允许主程序流程继续处理。

我知道异步调用可以在单个线程上执行,那这是如何实现的呢?


1
http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/3de8670c-49ca-400f-a1dc-ce3a3619357d - santosh singh
1
JavaScript没有线程,但它有异步方法调用。 - Ajedi32
1
任何一个系统都可以由一个进程管理多个子进程,控制进程可以提供子进程的异步操作。在JavaScript中,浏览器提供计算流。当某些函数进行异步调用时,浏览器可以为该函数存储上下文。现在,同一线程可以更改上下文,以恢复执行另一个函数。而在传统的多线程程序中,一个线程执行一个函数会阻塞,以允许另一个线程运行不同的函数。每个线程同步执行自己的功能。 - Mike
我使用异步与多线程。 - iperov
我使用多线程来进行异步操作。 - undefined
10个回答

111
无论何时需要异步进行的操作不需要CPU做任何工作,那么这个操作就可以在不生成另一个线程的情况下完成。例如,如果异步操作是I/O,则CPU无需等待I/O完成。它只需要启动操作,然后可以继续进行其他工作,而I/O硬件(磁盘控制器,网络接口等)则会执行I/O工作。硬件通过中断CPU来通知它何时完成,然后操作系统将事件传递给您的应用程序。
通常,更高级别的抽象和API不会公开来自操作系统和底层硬件的底层异步API。在这些情况下,即使生成的线程只是在等待I/O操作,也通常更容易创建线程来执行异步操作。
如果异步操作需要CPU执行工作,则通常必须在另一个线程中完成该操作,以便它真正成为异步操作。即使这样,只有当存在多个执行单元时,它才真正是异步的。

4
解释得很清楚,谢谢您。但我有一个问题。您提到:“例如,如果异步操作是I/O,则CPU无需等待I/O完成,只需要启动操作。”我的问题是,当程序是单线程的,并且在代码的第三行调用了一个I/O操作,那么如何在第三行中启动操作并执行第四行而不等待I/O操作完成?对于我来说,必须将第三行的代码放在一个新线程中,才能实现第四行在I/O操作完成前继续执行。【Java程序视角】 - spiderman
2
尽管 CPU 不必等待,但它仍会等待 I/O 操作完成...我相信你的第二段回答了我的问题。在这种情况下,我需要得出结论,在 Java 中,异步调用需要在不同的线程中执行。如果我错了,请纠正我,或者让我知道是否需要发布一个新的 SO 问题。 - spiderman
@spiderman 有些语言,比如Node.js,采用异步编程模型。该语言和运行时提供内置功能,允许在IO操作完成之前,执行第4行代码在同一线程中执行。这是通过第3行提供回调函数来实现的,运行时将在IO完成时调用该回调函数。 - jmrah
@spiderman 也许……操作系统的异步函数直接返回 false 或其他什么值。 - Byeongin Yoon

87

这个问题过于笼统,难以回答。

一般情况下,异步调用并不一定创建一个新线程。这是一种实现方式,预先存在的线程池或外部进程是其他方式。它严重依赖于语言、对象模型(如果有)和运行时环境。

异步只是表示调用线程不会坐等响应,也不会在调用线程中进行异步操作。

除此之外,你需要更具体的信息。


9
基本上,我的理解是: 多线程 == 使用多个线程以提供处理性能的好处,尤其是当任务可以从多个处理器中受益时,以及在异步情况下获得好处。 异步 == 一个进程执行它自己的任务,而调用该进程的状态不必等待其完成。这可能不一定使用多个线程来实现,例如其他硬件组件可能会承担责任。 - Evan Sevy
7
@Michael - 你能否举个例子解释一下如何在单线程中进行异步编程? - Kumar Vaibhav
7
@KumarVaibhav - 最常见的例子是单线程从队列中获取项目(例如Windows消息队列)。 如果程序习惯将项发送到自己的队列中(一种常见模式),那么发送项的代码片段不会等待操作完成,而是直接返回。操作将在主循环中适时处理。 - Michael Kohne
3
代码的编写方式和实际执行方式可能存在差异。例如,在C#中,我可以编写一个启动异步任务的方法。我的方法完全支持异步操作,可以在等待任务完成的同时执行其他操作。然而,CLR也可能决定内联我的任务并同步执行它。 - Mike
哪个线程执行等待的任务?标记为异步的方法在执行到 await 关键字之前是同步执行的,此时哪个线程执行这个可等待的任务? - user2156081

21

不,异步调用并不总是涉及线程。

通常情况下,它们会启动某种操作,该操作与调用方同时进行。但该操作可能由另一个进程、操作系统、其他硬件(如磁盘控制器)、网络上的其他计算机或人类处理。线程并不是实现并行处理的唯一方式。


15

JavaScript是单线程且异步的。例如,当您使用XmlHttpRequest时,会提供一个回调函数,该函数将在响应返回时异步执行。

John Resig对JavaScript中计时器的工作原理有很好的解释。


14

多线程指的是在同一进程中发生多个操作。而异步编程则涉及到跨进程。例如,如果我的操作调用了一个Web服务,线程不需要等待Web服务返回。这里我们使用异步编程来允许线程不等待另一台机器上的进程完成。当它开始从Web服务获取响应时,它可以中断主线程以表示Web服务已经完成处理请求。现在主线程可以处理结果。


我有点不同意。我写了一个单线程的HTTP服务器,使用异步IO完成处理多个同时请求。异步并不需要在多个执行路径中发生事情,它只是意味着多个计算流可以重叠。另一种看待它的方式是,在单线程的操作系统上,我可以同时运行2个进程。从每个进程的角度来看,它们是同步运行的。但是,从操作系统的角度来看,它是异步运行的。 - Mike
在找到一个承认线程和同步是两个不同概念的答案之前,我必须滚动太远了。 - MrYellow

12

自非抢占式时代(版本2.13、3.0、3.1等)以来,Windows一直使用消息循环进行异步处理,早在支持真正的线程之前。所以回答你的问题,不需要创建线程来执行异步处理。


@dmckee - 有趣的是,不同的系统以类似的方式发展。 - Otávio Décio

8
异步调用甚至不需要发生在调用方的同一系统/设备上。因此,如果问题是,异步调用是否需要当前进程中的线程,答案是否定的。然而,必须要有一个执行线程在某处处理异步请求。
"线程执行"是一个模糊的术语。在合作任务系统(例如早期的Macintosh和Windows操作系统)中,线程执行可以简单地是发出请求的同一进程运行另一个堆栈、指令指针等。但是,当人们通常谈论异步调用时,它们通常意味着由另一个线程处理的调用(如果它是进程内的,则是在同一进程内;如果它是进程间的,则是由另一个进程处理)。
请注意,进程间(或进程间)通信(IPC)通常概括为包括进程内通信,因为锁定和同步数据的技术通常是相同的,无论这些线程的执行在哪个进程中运行。

7

有些系统允许您利用内核的并发性来使用回调函数实现某些功能。例如,在 Mac System 6-8 的非抢占式多任务处理器时代,异步IO回调被用于实现非阻塞互联网服务器。

这样,您可以在程序中拥有并发执行流,而无需像线程一样使用它们。


5

异步只是指您不会阻塞程序等待某些东西(函数调用、设备等)完成。它可以在单独的线程中实现,但通常使用专用线程进行同步任务,并通过某种事件系统进行通信,从而实现类似异步的行为。

有单线程异步程序的示例。像这样的东西:

...do something
...send some async request
while (not done)
    ...do something else
    ...do async check for results

2
异步调用的本质是,如果您希望应用程序在调用正在进行时继续运行,则需要 生成 一个新线程,或者至少 利用 另一个仅用于处理异步回调的线程。
有时,根据情况,您可能希望调用异步方法,但让它对用户呈现为同步(即阻塞,直到异步方法发出完成信号)。这可以通过 Win32 API(例如WaitForSingleObject)来实现。

这在某些系统上是正确的,但并非所有系统都是如此。Unix不需要您生成或利用另一个线程,除非您将内核调用另一个线程,我想这也是一种看待它的方式。 - Craig S
这在Windows上也不是真的。例如,重叠I/O是异步的。 - Jason Orendorff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接