延续-C#5异步/等待-是并发*吗?

我一直在考虑C#5中的新异步内容,然后出现一个特定问题。

我知道Task关键字是一个精巧的编译器技巧/语法糖,用于实现连续传递,该方法的其余部分分解为Application.DoEvents()对象,并按顺序排队等待运行,但控制权返回给调用方法 。

我的问题是,我听说目前所有这些都在一个线程上。 这是否意味着异步工作实际上只是一种将延续代码转换为Task对象,然后在每个任务完成之后调用Application.DoEvents()的方法,然后再开始下一个任务?

还是我错过了什么? (问题的这一部分是修辞性的-我完全知道我丢失了一些东西:))

Neil Barnwell asked 2019-11-08T03:58:22Z
4个解决方案
52 votes

从某种意义上说,它是并发的,可以随时进行许多未完成的异步操作。 它可能是也可能不是多线程的。

默认情况下,ConfigureAwait将继续时间安排回“当前执行上下文”。 如果不是null,则“当前执行上下文”定义为2604626034800700722945,如果没有SynchronizationContext,则定义为TaskScheduler.Current

您可以通过调用ConfigureAwait并为DoEvents参数传递false来覆盖此默认行为。 在这种情况下,将不会将继续调度回该执行上下文。 这通常意味着它将在线程池线程上运行。

除非您正在编写库代码,否则默认行为正是您所期望的。 WinForms,WPF和Silverlight(即所有UI框架)都提供了2604626034817500500160,因此延续在UI线程上执行(并可以安全地访问UI对象)。 ASP.NET还提供了DoEvents,以确保继续在正确的请求上下文中执行。

其他线程(包括线程池线程DoEventsDoEvents)不提供SynchronizationContext。因此,默认情况下,控制台应用程序和Win32服务根本没有async Task。 在这种情况下,继续在线程池线程上执行。 这就是为什么使用await/async的控制台应用程序演示包括对Console.ReadLine/ReadKey的调用或对Task的阻止Wait的调用。

如果发现自己需要DoEvents,则可以使用Nito.AsyncEx库中的DoEvents; 它基本上只提供了兼容DoEvents的“主循环”和SynchronizationContext。我发现它对控制台应用程序和单元测试很有用(VS2012现在已内置对async Task单元测试的支持)。

有关DoEvents的更多信息,请参见我的2月MSDN文章。

任何时候都不会是DoEvents或类似的名称; 相反,控制流将完全返回,并且继续(函数的其余部分)计划在以后运行。 这是一个更加干净的解决方案,因为它不会像使用DoEvents那样引起再入问题。

Stephen Cleary answered 2019-11-08T03:59:30Z
5 votes

async / await背后的整个思想是,它很好地执行了连续传递,并且没有为该操作分配新的线程。 延续可能发生在新线程上,也可能继续在同一线程上。

Polynomial answered 2019-11-08T03:59:58Z
2 votes

异步/等待的真正的“肉”(异步)部分通常是单独完成的,与呼叫者的通信是通过TaskCompletionSource完成的。 如此处所写[http://blogs.msdn.com/b/pfxteam/archive/2009/06/02/9685804.aspx]

TaskCompletionSource类型有两个相关用途,两者的名称均被提及:它是创建任务的源,以及该任务完成的源。 本质上,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,您可以访问可以等待的TaskCompletionSource对象,但是不能通过创建多线程的async / await关键字访问。

请注意,当许多“慢速”函数将转换为异步/等待语法时,您将不需要太多使用TaskCompletionSource。 他们将在内部使用它(但最后必须有TaskCompletionSource才能产生异步结果)

xanatos answered 2019-11-08T04:00:53Z
1 votes

我想解释的方式是,“ await”关键字只是等待任务完成,而在等待时却让调用线程执行。 然后,它返回Task的结果,并在Task完成后从“ await”关键字之后的语句继续。

我注意到的某些人似乎认为Task与调用线程在同一线程中运行,这是不正确的,可以通过尝试在等待调用的方法中更改Windows.Forms GUI元素来证明这一点。 但是,只要有可能,继续在调用线程中运行。

这只是在任务完成时不必具有回调委托或事件处理程序的一种巧妙方法。

Aaron Murgatroyd answered 2019-11-08T04:01:35Z
translate from https://stackoverflow.com:/questions/7663101/c-sharp-5-async-await-is-it-concurrent