2014-02-19 170 views
21

我很好奇异步等待线程内部结构。异步等待线程内部结构

每个人都表示异步在性能方面比较好,因为它释放了正在等待长时间异步调用响应的线程。 好吧,我明白了。

但让我们来考虑这种情况。

我有一个异步方法在数据库上执行异步操作。 数据库的api暴露函数BeginQuery和事件QueryCompleted。 我用一个任务(使用TaskCompletionSource)包装了它们。

我的问题是调用BeginQuery和触发事件QueryCompleted之间的内幕。

我的意思是 - 是否需要培育某种工作人员来开展活动?在非常低的级别,它必须是一些同步循环,阻止来自db的线程读取结果。

我想,任何异步调用都必须生成一个线程来实际处理响应(也许等待它在驱动程序代码中的低级C++循环中)。

所以我们唯一的“收获”就是当其他线程正在工作时,调用者线程可以被释放。

调用异步方法是否总是创建一个新的工作线程?

有人可以证实我的理解?

+4

我不认为“每个人都表示异步在性能方面更好”。异步/等待可以使应用程序更具响应性,因为它可以更轻松地使用用户界面的任务和延续。 – Dirk

+0

异步/等待是比线程更高层次的抽象。这些操作在SynchronizationContext上同步,并且引擎的实现可能会或可能不会创建额外的线程。见例如[这是所有关于SynchronizationContext](http://msdn.microsoft.com/en-us/magazine/gg598924.aspx)。 – GSerg

回答

33

每个人都指出,异步是如此美好的性能的情况下,因为它释放正在等待以久的异步调用的响应线程。

是和否。 async背后的要点是释放调用线程。在UI应用程序中,async的主要好处是响应性,因为UI线程被释放。在服务器应用程序中,async的主要好处是可伸缩性,因为请求线程被释放以处理其他请求。

所以我们唯一的“收获”就是调用者线程可以在其他线程正在工作时被释放。 总是调用一个异步方法创建一个新的工作线程?

否。在OS级别,所有I/O都是异步的。正在进行底层异步I/O时,它是阻塞线程的同步API。我最近在博客文章中写到:there is no thread

+0

谢谢你的回答。与链接的博客一起,这是非常全面的讲座。但我有个问题。如果我想编写一个利用异步I/O的库,我该从哪里开始? 我是否需要编写一些非托管代码?这可以纯粹用C#完成吗? – mbudnik

+0

它可以在纯粹的托管代码中完成,但是这样做的指示很难找到。 [这是最好的概述](http://www.beefycode.com/post/Using-Overlapped-IO-from-Managed-Code.aspx)。 –

+0

Bumping [没有线程](http://blog.stephencleary.com/2013/11/there-is-no-thread.html) – bvj

1

异步方法不创建新的线程,它们是基于Task s,这可能会或可能不会使用线程(如TaskCompletionSource),因为他们使用ThreadPool,即使他们这样做有没有开销。

3

我的意思是 - 是否需要产生某种工作人员来触发 事件?在非常低的级别,它必须是一些同步循环,即 阻止来自db的线程读取结果。

即使你确实有等待内核对象(如手动重置事件),你仍然可以从堵转阻塞同步代码到一个异步和释放线程(更新:a real-life scenario)。

例如,同步代码:

void Consume() 
{ 
    var completedMre = new ManualResetEvent(false); 

    producer.StartOperation(completedMre); 

    completedMre.WaitOne(); // blocking wait 

    Console.WriteLine(producer.DequeueResult()); 
} 

异步模拟:

async Task ConsumeAsync() 
{ 
    var completedMre = new ManualResetEvent(false); 

    producer.StartOperation(completedMre); 

    var tcs = new TaskCompletionSource<Result>(); 

    ThreadPool.RegisterWaitForSingleObject(completedMre, 
     (s, t) => tcs.SetResult(producer.DequeueResult()), 
     null, Timeout.Infinite, true); 

    var result = await tcs.Task; 
    Console.WriteLine(result); 
} 

异步版本秤更好最多64倍(MAXIMUM_WAIT_OBJECTS,这是其可以是内核对象的最大数目通过RegisterWaitForSingleObject汇总,用于在单个线程上等待)。所以,你可以并行调用Consume() 64次,它会阻塞64个线程。或者你可以拨打ConsumeAsync 64次,它只会阻塞一个线程。

5

我的意思是 - 是否需要产生某种工作人员来触发 事件?在非常低的级别,它必须是一些同步循环,即 阻止来自db的线程读取结果。

它将创建一个IO完成端口(IOCP),表示正在外部处理的任务,该线程将继续执行其他任务。然后,当IOCP通知任务完成时,一个线程将接收IOCP的状态并继续执行任务。

http://www.drdobbs.com/cpp/multithreaded-asynchronous-io-io-comple/201202921

I/O完成端口提供了一个优雅的解决方案的 编写使用多线程和 异步I/O可扩展的服务器应用程序的问题。

+1

This!在操作系统上使用IOCP比完整的线程更容易。您的操作系统只能处理几千个线程,但可以轻松处理几百万个完成端口。因此,通过异步/等待,您可以限制大量繁重线程的数量,并使用更轻松处理的称为I/O完成端口的轻量级操作系统对象。 –