如何在没有异步CTP的情况下实现await
您将如何实现与 Async CTP await
关键字类似的功能?是否有一个在所有情况下都像 await
一样工作的简单实现,或者 await
是否需要针对不同场景使用不同的实现?
How would you implement something that works similarly to the Async CTP await
keyword? Is there a simple implementation that works like await
in all cases, or does await
require different implementations for different scenarios?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
由迭代器组成的协程的实现和示例很少(yield)。
其中一个例子是 Caliburn.Micro 框架,它使用这种模式进行异步 GUI 操作。但它可以很容易地推广到一般的异步代码。
There are few implementations and examples of coroutines made out of iterators (yield).
One of the examples is Caliburn.Micro framework, that uses this patter for asynchronous GUI operations. But it can easily be generalised for general async code.
MindTouch DReAM 框架在迭代器模式之上实现了协程,其功能与 Async/Await 非常相似
: .
Result
是 DReAM 的Task
版本。框架 dll 可与 .NET 2.0+ 配合使用,但要构建它,您需要 3.5,因为现在我们使用大量 3.5 语法。The MindTouch DReAM framework implements Coroutines on top of the Iterator pattern which is functionally very similar to Async/Await:
vs.
Result
is DReAM's version ofTask
. The framework dlls work with .NET 2.0+, but to build it you need 3.5, since we're using a lot of 3.5 syntax these days.Microsoft 的 Bill Wagner 在 MSDN 杂志上撰写了一篇文章,介绍了如何使用Visual Studio 2010 中的任务并行库可实现类似异步的行为,而无需添加对异步 ctp 的依赖项。
它广泛使用
Task
和Task
,这还有一个额外的好处,即一旦 C# 5 发布,您的代码将做好准备开始使用async< /code> 和
await
。Bill Wagner from Microsoft wrote an article in MSDN Magazine about how you can use the Task Parallel Library in Visual Studio 2010 to implement async like behavior without adding a dependency on the async ctp.
It uses
Task
andTask<T>
extensively which also has the added benefit that once C# 5 is out, your code will be well prepared to start usingasync
andawait
.根据我的阅读,
yield return
和await
之间的主要区别在于await
可以显式地将新值返回到延续中。而使用
yield return
时,您必须通过引用来完成同样的事情。From my reading, the major differences between
yield return
andawait
is thatawait
can provide explicitly return a new value into the continuation.whereas with
yield return
, you'd have to accomplish the same thing by reference.新的
await
关键字与现有的yield return
关键字具有相似的语义,因为它们都会导致编译器为您生成延续样式状态机。因此,可以使用与异步 CTP 具有某些相同行为的迭代器将某些内容组合在一起。这是它的样子。
由于
yield return
生成一个IEnumerable
,因此我们的协程必须返回一个IEnumerable
。所有的魔法都发生在AsyncHelper.Invoke
方法内。这就是我们的协程(伪装成被黑的迭代器)运行的原因。需要特别注意确保迭代器始终在当前同步上下文(如果存在)上执行,这在尝试模拟await
在 UI 线程上的工作方式时非常重要。它通过同步执行第一个MoveNext
操作,然后使用SynchronizationContext.Send
从工作线程执行其余操作来实现此目的,该线程也用于异步等待各个步骤。关于
TaskCompletionSource
的全部内容是我尝试复制await
可以“返回”值的方式。问题是协程必须实际返回一个IEnumerable
,因为它只不过是一个被黑的迭代器。所以我需要想出一种替代机制来捕获返回值。这有一些明显的局限性,但我希望这能给您一个总体思路。它还演示了 CLR 如何拥有一种通用机制来实现协程,其中
await
和yield return
将普遍使用,但以不同的方式提供各自的语义。The new
await
keyword has similar semantics to the existingyield return
keyword in that they both cause the compiler to generate the continuation style state machine for you. So it is possible to hack something together using iterators that has some of the same behaviors as the Async CTP.Here is what it would look like.
Since
yield return
generates anIEnumerable
our coroutine must return anIEnumerable
. All of the magic happens inside theAsyncHelper.Invoke
method. This is what gets our coroutine (masquerading as a hacked iterator) going. It takes special care to make sure the iterator is always executed on the current synchronization context if one exists which is important when trying to simulate howawait
works on a UI thread. It does this by executing the firstMoveNext
synchronously and then usingSynchronizationContext.Send
to do the rest from a worker thread which is also used to asynchronously wait on the individual steps.The whole bit about the
TaskCompletionSource
was my attempt at replicating the wayawait
can "return" a value. The problem is that the coroutine has to actually return anIEnumerable
since it is nothing more than a hacked iterator. So I needed to come up with an alternate mechanism to capture a return value.There are some glaring limitations with this, but I hope this gives you the general idea. It also demonstrates how the CLR could have one generalized mechanism for implementing coroutines for which
await
andyield return
would use ubiquitously, but in different ways to provide their respective semantics.await
总是涉及相同类型的转换 - 但这是一个非常痛苦的过程。await
的库端并不太复杂,但棘手的一点是编译器为您构建一个状态机,允许继续跳回正确的位置。可能,我对迭代器块(yield return)的一些黑客使用可以伪造类似的东西......但它会非常难看。
几周前,我举办了一个 DevExpress 网络研讨会,介绍编译器在幕后所做的事情 - 显示了反编译几个示例中的代码,以及解释编译器如何构建要返回的任务,以及“等待者”必须做什么。它可能对你有用。
await
always involves the same kind of transformation - but it's a pretty painful one. The library side ofawait
isn't too complicated, but the tricky bit is that the compiler builds a state machine for you, allowing the continuation to jump back to the right place.It's possible that my some hacky use of iterator blocks (yield return) you could fake something similar... but it would be pretty ugly.
I gave a DevExpress webinar on what the compiler is doing behind the scenes a few weeks ago - that shows decompiled code from a couple of examples, as well as explaining how the compiler builds a task to return, and what the "awaiter" has to do. It may be useful to you.