C#5.0的异步等待功能与TPL有何不同?

zil*_*n01 61 c# task-parallel-library async-await c#-5.0

我没有看到C#(和VB)的新异步功能和.NET 4.0的任务并行库之间存在差异.举个例子来说,埃里克利珀的代码从这里:

async void ArchiveDocuments(List<Url> urls) {
    Task archive = null;
    for(int i = 0; i < urls.Count; ++i) {
        var document = await FetchAsync(urls[i]);
        if (archive != null)
            await archive;
        archive = ArchiveAsync(document);
    }
}
Run Code Online (Sandbox Code Playgroud)

似乎该await关键字有两个不同的用途.第一个出现(FetchAsync)似乎意味着,"如果稍后在方法中使用此值并且其任务未完成,请等到它完成后再继续." 第二个实例(archive)似乎意味着,"如果此任务尚未完成,请立即等待直到完成." 如果我错了,请纠正我.

难道不能像这样容易写吗?

void ArchiveDocuments(List<Url> urls) {
    for(int i = 0; i < urls.Count; ++i) {
        var document = FetchAsync(urls[i]);       // removed await
        if (archive != null)
            archive.Wait();                       // changed to .Wait()
        archive = ArchiveAsync(document.Result);  // added .Result
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经将第await一个替换Task.Result为实际需要值的位置,第二个await替换Task.Wait()为实际发生等待的位置.该功能(1)已经实现,并且(2)在语义上与代码中实际发生的内容更加接近.

我确实意识到一个async方法被重写为状态机,类似于迭代器,但我也看不出它带来了什么好处.任何需要另一个线程运行的代码(例如下载)仍然需要另一个线程,任何不需要的代码(例如从文件中读取)仍然可以利用TPL只使用一个线程.

我显然遗失了一些巨大的东西; 任何人都可以帮助我更好地理解这一点吗?

Ree*_*sey 71

我认为这里出现了误解:

似乎await关键字有两个不同的用途.第一次出现(FetchAsync)似乎意味着,"如果稍后在方法中使用此值并且其任务未完成,请等到它完成后再继续." 第二个实例(存档)似乎意味着,"如果此任务尚未完成,请立即等待直到完成." 如果我错了,请纠正我.

这实际上是完全错误的.这两者具有相同的含义.

在你的第一个案例中:

var document = await FetchAsync(urls[i]);
Run Code Online (Sandbox Code Playgroud)

这里发生的是运行时说"开始调用FetchAsync,然后将当前执行点返回给调用此方法的线程." 这里没有"等待" - 相反,执行返回到调用同步上下文,并且事情继续搅动.在将来的某个时刻,FetchAsync的任务将完成,此时,此代码将在调用线程的同步上下文中恢复,并且将发生下一个语句(分配文档变量).

执行将继续,直到第二次等待调用 - 此时,同样的事情将发生 - 如果Task<T> (存档)未完成,执行将被释放到调用上下文 - 否则,将设置存档.

在第二种情况下,事情是非常不同的 - 在这里,你明确地阻塞,这意味着在整个方法完成之前,调用同步上下文永远不会有机会执行任何代码.当然,仍然存在异步,但是异步完全包含在这个代码块中 - 在此线程之外的代码之外不会发生任何代码,直到所有代码完成.


Dan*_*iel 25

这是个很大的差异:

Wait()块,await不阻止.如果ArchiveDocuments() 在GUI线程上运行异步版本,GUI将在提取和归档操作运行时保持响应.如果您使用TPL版本Wait(),您的GUI将被阻止.

注意,async设法在不引入任何线程的情况下执行此操作 - 在await控制点处,简单地返回到消息循环.一旦等待的任务完成,该方法的其余部分(继续)将在消息循环中排队,并且GUI线程将继续ArchiveDocuments在其停止的位置运行.


Bra*_*ham 24

在他所做的第9频道直播采访中,安德斯将其归结为一个非常简洁的答案.我强烈推荐它

新的Async和await关键字允许您在应用程序中协调并发.它们实际上并没有在您的应用程序中引入任何并发性.

TPL,更具体地说,Task是一种可以用来同时实际执行操作的方法.新的async和await关键字允许您以"同步"或"线性"方式组合这些并发操作.

因此,您仍然可以在程序中编写线性控制流,而实际计算可能会同时发生也可能不会同时发生.当计算同时发生时,await和async允许您组合这些操作.

  • 问题是"C#5.0的async-await功能与TPL有何不同?" 我的回答适合IMO这个问题 (13认同)
  • 这实际上并没有*说*什么,是吗?让我重新说一下:这是如何回答提出的问题? (3认同)

Joh*_*ren 6

将程序控制流程转换为状态机的能力使这些新关键词变得有意义.把它想象成控制而不是价值.

查看Anders的第9频道视频,了解新功能.