C# 5.0 的 async-await 特性与 TPL 有何不同? [英] How does C# 5.0's async-await feature differ from the TPL?

查看:13
本文介绍了C# 5.0 的 async-await 特性与 TPL 有何不同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看不出 C#(和 VB)的新异步功能与 .NET 4.0 的 任务并行库.例如,Eric Lippert 的代码 从这里:

I don't see the different between C#'s (and VB's) new async features, and .NET 4.0's Task Parallel Library. Take, for example, Eric Lippert's code from here:

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);
    }
}

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

It seems that the await keyword is serving two different purposes. The first occurrence (FetchAsync) seems to mean, "If this value is used later in the method and its task isn't finished, wait until it completes before continuing." The second instance (archive) seems to mean, "If this task is not yet finished, wait right now until it completes." If I'm wrong, please correct me.

难道就不能这么简单地写成这样吗?

Couldn't it just as easily be written like this?

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
    }
}

我已将第一个 await 替换为实际需要该值的 Task.Result,并将第二个 await 替换为 Task.Wait(),等待实际发生的地方.(1) 已经实现了功能,(2) 在语义上更接近代码中实际发生的事情.

I've replaced the first await with a Task.Result where the value is actually needed, and the second await with Task.Wait(), where the wait is actually occurring. The functionality is (1) already implemented, and (2) much closer semantically to what is actually happening in the code.

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

I do realize that an async method is rewritten as a state machine, similar to iterators, but I also don't see what benefits that brings. Any code that requires another thread to operate (such as downloading) will still require another thread, and any code that doesn't (such as reading from a file) could still utilize the TPL to work with only a single thread.

我显然在这里遗漏了一些重要的东西;有人能帮我更好地理解这一点吗?

I'm obviously missing something huge here; can anybody help me understand this a little better?

推荐答案

我认为误解出现在这里:

I think the misunderstanding arises here:

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

It seems that the await keyword is serving two different purposes. The first occurrence (FetchAsync) seems to mean, "If this value is used later in the method and its task isn't finished, wait until it completes before continuing." The second instance (archive) seems to mean, "If this task is not yet finished, wait right now until it completes." If I'm wrong, please correct me.

这实际上是完全错误的.这两个意思是一样的.

This is actually completely incorrect. Both of these have the same meaning.

在你的第一种情况下:

var document = await FetchAsync(urls[i]);

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

What happens here, is that the runtime says "Start calling FetchAsync, then return the current execution point to the thread calling this method." There is no "waiting" here - instead, execution returns to the calling synchronization context, and things keep churning. At some point in the future, FetchAsync's Task will complete, and at that point, this code will resume on the calling thread's synchronization context, and the next statement (assigning the document variable) will occur.

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

Execution will then continue until the second await call - at which time, the same thing will happen - if the Task<T> (archive) isn't complete, execution will be released to the calling context - otherwise, the archive will be set.

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

In the second case, things are very different - here, you're explicitly blocking, which means that the calling synchronization context will never get a chance to execute any code until your entire method completes. Granted, there is still asynchrony, but the asynchrony is completely contained within this block of code - no code outside of this pasted code will happen on this thread until all of your code completes.

这篇关于C# 5.0 的 async-await 特性与 TPL 有何不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆