C#5asynchronous/等待 – 它是*并发*?
我一直在考虑C#5中的新asynchronous的东西,并提出了一个特定的问题。
我知道await
关键字是一个简洁的编译器技巧/语法糖来实现continuation的传递 ,其余的方法被分解成Task
对象并排队等待运行,但是控制返回到调用方法。
我的问题是,我听说目前这一切都在一个单一的线程。 这是否意味着这种asynchronous的东西实际上只是将继续代码转换为Task
对象,然后在每个任务完成之后调用Application.DoEvents()
,然后再启动下一个任务?
还是我错过了什么? (问题的这一部分是修辞 – 我完全知道我错过了什么 :))
提前谢谢了。
这意味着许多优秀的asynchronous运行可能随时在进行。 它可能是也可能不是multithreading的 。
默认情况下, await
会将延续安排回“当前执行上下文”。 如果“当前执行上下文”是非null
,则定义为SynchronizationContext.Current
如果没有SynchronizationContext
,则定义为TaskScheduler.Current
。
您可以通过调用ConfigureAwait
并为continueOnCapturedContext
parameter passingfalse
来覆盖此默认行为。 在这种情况下,延续将不会被安排回执行上下文。 这通常意味着它将在一个线程池线程上运行。
除非你正在编写库代码,否则默认的行为正是你想要的。 WinForms,WPF和Silverlight(即所有的UI框架)提供了一个SynchronizationContext
,所以延续在UI线程上执行(并且可以安全地访问UI对象)。 ASP.NET还提供了一个SynchronizationContext
来确保继续在正确的请求上下文中执行。
其他线程(包括线程池线程, Thread
和BackgroundWorker
)不提供SynchronizationContext
。 因此,默认情况下,控制台应用程序和Win32服务根本没有SynchronizationContext
。 在这种情况下,继续在线程池线程上执行。 这就是为什么使用await
/ async
控制台应用程序演示包含对Console.ReadLine
/ ReadKey
的调用,或者在Task
上执行阻塞Wait
。
如果你发现自己需要一个SynchronizationContext
,你可以使用我的Nito.AsyncEx库中的AsyncContext
; 它基本上只是提供了一个与SynchronizationContext
“主循环”。 我发现它适用于控制台应用程序和unit testing (VS2012现在已经内置了对async Task
unit testing的支持)。
有关SynchronizationContext
更多信息,请参阅我的二月MSDN文章 。
DoEvents
或相当于所谓的“ 相反,控制stream完全返回 ,而延续(函数的其余部分)计划稍后运行。 这是一个更清洁的解决scheme,因为它不会像使用DoEvents
那样会导致重入问题。
async / await背后的全部思想是,它很好地执行continuation,并且不为操作分配一个新的线程。 继续可能发生在一个新的线程,它可能会继续在同一个线程。
asynchronous/等待的真正“肉”(asynchronous)部分通常是单独完成的,并且与调用者的通信通过TaskCompletionSource完成。 如此处所写:http://blogs.msdn.com/b/pfxteam/archive/2009/06/02/9685804.aspx
TaskCompletionSourcetypes提供了两个相关的用途,都是通过名称来提及的:它是创build任务的来源,以及该任务完成的来源。 本质上,TaskCompletionSource充当Task的生产者及其完成。
这个例子很明显:
public static Task<T> RunAsync<T>(Func<T> function) { if (function == null) throw new ArgumentNullException(“function”); var tcs = new TaskCompletionSource<T>(); ThreadPool.QueueUserWorkItem(_ => { try { T result = function(); tcs.SetResult(result); } catch(Exception exc) { tcs.SetException(exc); } }); return tcs.Task; }
通过TaskCompletionSource
您可以访问可以等待的Task
对象,但不是通过创buildmultithreading的async / await关键字。
请注意,当许多“慢”function将被转换为asynchronous/等待语法时,您将不需要非常多地使用TaskCompletionSource
。 他们将在内部使用它(但最终必须有一个TaskCompletionSource
有一个asynchronous的结果)
我喜欢解释它的方式是,“await”关键字只是等待一个任务完成,但在等待的时候执行到调用线程。 然后它返回任务的结果,并在任务完成后从“await”关键字之后的语句继续。
我注意到一些人似乎认为任务是在与调用线程相同的线程中运行,这是不正确的,可以通过尝试更改等待调用的方法中的Windows.Forms GUI元素来certificate。 但是,继续在可能的调用线程中运行。
它只是一个完美的方式,无需为任务完成时的callback委托或事件处理程序。