在另一个线程中运行异步函数

Soo*_*nts 17 .net c# c#-4.0 async-ctp

我正在评估Async CTP.

如何在另一个线程池的线程上开始执行异步函数?

static async Task Test()
{
    // Do something, await something
}

static void Main( string[] args )
{
    // Is there more elegant way to write the line below?
    var t = TaskEx.Run( () => Test().Wait() );

    // Doing much more in this same thread
    t.Wait(); // Waiting for much more then just this single task, this is just an example
}
Run Code Online (Sandbox Code Playgroud)

The*_*ung 50

我是新的(我的处女帖子)Stack Overflow,但是我很高兴你问起Async CTP,因为我正在微软的团队中工作:)

我想我明白你的目标是什么,并且有一些你正确的事情,让你到那里.

我想你想要什么:

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
}
Run Code Online (Sandbox Code Playgroud)

Task.Run与Task.RunEx

由于此CTP安装在.NET 4.0之上,因此我们不想修补mscorlib中的实际 System.Threading.Tasks.Task类型.相反,操场API在冲突时被命名为FooEx.

我们为什么要说出其中一些Run(...)和一些RunEx(...)?原因是由于我们在发布CTP时尚未完成的方法重载的重新设计.在我们当前工作的代码库,我们实际上不得不调整的C#方法重载规则稍微使正确的事情发生了异步lambda表达式-可以返回void,TaskTask<T>.

问题是当异步方法或lambdas返回Task或者Task<T>它们实际上在返回表达式中没有外部任务类型时,因为该任务是作为方法或lambda调用的一部分自动生成的.在我们看来,这对于代码清晰度来说似乎是正确的体验,尽管之前确实使事情变得非常不同,因为通常返回语句的表达式可以直接转换为方法或lambda的返回类型.

因此,异步voidlambdas和异步Tasklambdas都支持return;不带参数.因此需要澄清方法重载决策以决定选择哪一个.因此,您同时拥有Run(...)和RunEx(...)的唯一原因是,在PDC 2010命中时,我们将确保为Async CTP的其他部分提供更高质量的支持.


如何考虑异步方法/ lambdas

我不确定这是否是一个混乱点,但我想我会提到它 - 当你编写异步方法或异步lambda时,它可以承担调用它的任何人的某些特征.这取决于两件事:

  • 您正在等待的类型
  • 并且可能是同步上下文(取决于上面)

等待的CTP设计和我们当前的内部设计都是基于模式的,因此API提供商可以帮助充实您可以"等待"的一系列充满活力的东西.这可能会根据您正在等待的类型而有所不同,其常见类型是Task.

Task等待实现是非常合理的,并且按照当前的线程SynchronizationContext来决定如何推迟工作.如果您已经在WinForms或WPF消息循环中,那么您的延迟执行将返回到同一消息循环(就像您使用BeginInvoke()"其余方法").如果你等待一个任务并且你已经在.NET线程池中,那么"你的方法的其余部分"将在其中一个线程池线程上恢复(但不一定完全相同),因为它们被合并开始并且您很可能很乐意使用第一个可用的池线程.


关于使用Wait()方法的注意事项

在您使用的样本中: var t = TaskEx.Run( () => Test().Wait() );

这样做是:

  1. 在周围的线程中同步调用TaskEx.Run(...)在线程池上执行lambda.
  2. 为lambda指定了一个线程池线程,它调用你的异步方法.
  3. 从lambda调用异步方法Test().因为lambda正在线程池上执行,所以Test()中的任何延续都可以在线程池中的任何线程上运行.
  4. lambda实际上并没有腾出那个线程的堆栈,因为它没有等待它.在这种情况下,TPL的行为取决于在Wait()调用之前Test()是否实际完成.但是,在这种情况下,您可能会在等待Test()完成在不同线程上执行时阻塞线程池线程.

'await'运算符的主要好处是它允许您添加稍后执行的代码 - 但不会阻塞原始线程.在线程池的情况下,您可以实现更好的线程利用率.

如果您对VB或C#的Async CTP有其他疑问,请告诉我,我很想听听他们:)


Jon*_*eet 5

通常由方法返回Task以确定它运行的位置,如果它正在开始真正的新工作而不是仅仅捎带其他东西.

在这种情况下,它看起来并不像你真的希望该Test()方法是异步的 - 至少,你没有使用它是异步的事实.你只是在另一个线程中启动东西...该Test()方法可以完全同步,你可以使用:

Task task = TaskEx.Run(Test);
// Do stuff
t.Wait();
Run Code Online (Sandbox Code Playgroud)

这不需要任何异步CTP优点.