我是 Stack Overflow 的新手(我的第一篇文章),但我很高兴您询问有关异步 CTP 的问题,因为我是 Microsoft 团队中的一员:)
我想我明白你的目标是什么,并且你正在做一些正确的事情来帮助你实现目标。
我认为你想要什么:
static async Task Test()
{
// Do something, await something
}
static void Main(string[] args)
{
// In the CTP, use Task.RunEx(...) to run an Async Method or Async Lambda
// on the .NET thread pool
var t = TaskEx.RunEx(Test);
// the above was just shorthand for
var t = TaskEx.RunEx(new Func<Task>(Test));
// because the C# auto-wraps methods into delegates for you.
// Doing much more in this same thread
t.Wait(); // Waiting for much more then just this single task, this is just an example
}
Task.Run 与 Task.RunEx
因为此 CTP 安装在 .NET 4.0 之上,所以我们不想修补actual System.Threading.Tasks.Task
输入 mscorlib。相反,当 Playground API 发生冲突时,它们会被命名为 FooEx。
为什么我们给其中一些命名Run(...)
和一些RunEx(...)
?原因是我们发布 CTP 时尚未完成方法重载的重新设计。在我们当前的工作代码库中,我们实际上必须稍微调整 C# 方法重载规则,以便 Async Lambda 发生正确的事情 - 它可以返回void
, Task
, or Task<T>
.
问题是当异步方法或 lambda 返回时Task
or Task<T>
,它们实际上在返回表达式中没有外部任务类型,因为任务是作为方法或 lambda 调用的一部分自动为您生成的。在我们看来,这似乎是代码清晰性的正确体验,尽管这确实使事情变得非常不同,因为通常 return 语句的表达式可以直接转换为方法或 lambda 的返回类型。
因此,两者都是异步的void
lambda 和异步Task
lambda 支持return;
没有争论。因此,需要对方法重载决策进行澄清,以决定选择哪一个。因此,同时拥有 Run(...) 和 RunEx(...) 的唯一原因是,我们可以确保在 PDC 2010 到来时为异步 CTP 的其他部分提供更高质量的支持。
如何考虑异步方法/lambda
我不确定这是否是一个混淆点,但我想我应该提到这一点 - 当您编写异步方法或异步 lambda 时,它可以呈现调用它的人的某些特征。这是基于两件事:
- 您正在等待的类型
- 可能还有同步上下文(取决于上面)
wait 的 CTP 设计和我们当前的内部设计都非常基于模式,因此 API 提供商可以帮助充实一组您可以“等待”的充满活力的事物。这可能会根据您正在等待的类型而有所不同,常见的类型是Task
.
Task
的await实现非常合理,并且遵循当前线程的SynchronizationContext
决定如何推迟工作。如果您已经处于 WinForms 或 WPF 消息循环中,那么您的延迟执行将在同一消息循环中返回(就像您使用BeginInvoke()
“你的方法的其余部分”)。如果您等待一个任务并且您已经在 .NET 线程池上,那么“方法的其余部分”将在其中一个线程池线程(但不一定是完全相同的线程)上恢复,因为它们一开始就被池化并且您很可能很乐意使用第一个可用的池线程。
使用 Wait() 方法的注意事项
在您的示例中您使用了:var t = TaskEx.Run( () => Test().Wait() );
其作用是:
- 在周围线程中同步调用 TaskEx.Run(...) 在线程池上执行 lambda。
- 为 lambda 指定一个线程池线程,它调用您的异步方法。
- 异步方法 Test() 是从 lambda 调用的。因为 lambda 是在线程池上执行的,所以 Test() 内的任何延续都可以在线程池中的任何线程上运行。
- lambda 实际上并没有腾出该线程的堆栈,因为它没有等待。在这种情况下,TPL 的行为取决于 Test() 是否在 Wait() 调用之前实际完成。但是,在这种情况下,当线程池线程等待 Test() 在另一个线程上完成执行时,您很可能会阻塞该线程。
这是“await”运算符的主要好处是它允许您添加稍后执行的代码 - 但不会阻塞原始线程。在线程池的情况下,可以实现更好的线程利用率。
如果您对 VB 或 C# 的异步 CTP 有其他疑问,请告诉我,我很乐意听听:)