简体   繁体   English

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

[英]Run an async function in another thread

I'm evaluating the Async CTP. 我正在评估Async CTP。

How can I begin execution of an async function on another thread pool's thread? 如何在另一个线程池的线程上开始执行异步函数?

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
}

I'm new (my virginal post) to Stack Overflow, but I'm jazzed that you're asking about the Async CTP since I'm on the team working on it at Microsoft :) 我是新的(我的处女帖子)Stack Overflow,但是我很高兴你问起Async CTP,因为我正在微软的团队中工作:)

I think I understand what you're aiming for, and there's a couple of things you're doing correctly, to get you there. 我想我明白你的目标是什么,并且有一些你正确的事情,让你到那里。

What I think you want: 我想你想要什么:

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 vs. Task.RunEx Task.Run与Task.RunEx

Because this CTP installs on top of .NET 4.0, we didn't want to patch the actual System.Threading.Tasks.Task type in mscorlib. 因为这个CTP安装在.NET 4.0之上,所以我们不想在mscorlib中修补实际的 System.Threading.Tasks.Task类型。 Instead, the playground APIs are named FooEx when they conflicted. 相反,当它们冲突时,操场API被命名为FooEx。

Why did we name some of them Run(...) and some of the RunEx(...) ? 为什么我们命名其中一些Run(...)和一些RunEx(...) The reason is because of redesigns in method overloading that we hadn't completed yet by the time we released the CTP. 原因是由于我们在发布CTP时尚未完成的方法重载的重新设计。 In our current working codebase, we've actually had to tweak the C# method overloading rules slightly so that the right thing happens for Async Lambdas - which can return void , Task , or Task<T> . 在我们当前的工作代码库中,我们实际上必须稍微调整C#方法重载规则,以便Async Lambdas正确发生 - 它可以返回voidTaskTask<T>

The issue is that when async method or lambdas return Task or Task<T> , they actually don't have the outer task type in the return expression, because the task is generated for you automatically as part of the method or lambda's invocation. 问题是,当异步方法或lambdas返回TaskTask<T> ,它们实际上在返回表达式中没有外部任务类型,因为任务是作为方法或lambda调用的一部分自动生成的。 This strongly seems to us like the right experience for code clarity, though that does make things quite different before, since typically the expression of return statements is directly convertible to the return type of the method or lambda. 在我们看来,这对于代码清晰度来说似乎是正确的体验,尽管之前确实使事情变得非常不同,因为通常返回语句的表达式可以直接转换为方法或lambda的返回类型。

So thus, both async void lambdas and async Task lambdas support return; 因此,async void lambdas和async Task lambdas都支持return; without arguments. 没有争论。 Hence the need for a clarification in method overload resolution to decide which one to pick. 因此需要澄清方法重载决策以决定选择哪一个。 Thus the only reason why you have both Run(...) and RunEx(...) was so that we would make sure to have higher quality support for the other parts of the Async CTP, by the time PDC 2010 hit. 因此,您同时拥有Run(...)和RunEx(...)的唯一原因是,在PDC 2010命中时,我们将确保为Async CTP的其他部分提供更高质量的支持。


How to think about async methods/lambdas 如何考虑异步方法/ lambdas

I'm not sure if this is a point of confusion, but I thought I'd mention it - when you are writing an async method or async lambda, it can take on certain characteristics of whoever is invoking it. 我不确定这是否是一个混乱点,但我想我会提到它 - 当你编写异步方法或异步lambda时,它可以承担调用它的任何人的某些特征。 This is predicated on two things: 这取决于两件事:

  • The type on which you are awaiting 您正在等待的类型
  • And possibly the synchronization context (depending on above) 并且可能是同步上下文(取决于上面)

The CTP design for await and our current internal design are both very pattern-based so that API providers can help flesh out a vibrant set of things that you can 'await' on. 等待的CTP设计和我们当前的内部设计都是基于模式的,因此API提供商可以帮助充实您可以“等待”的一系列充满活力的东西。 This can vary based on the type on which you're awaiting, and the common type for that is Task . 这可能会根据您正在等待的类型而有所不同,其常见类型是Task

Task 's await implementation is very reasonable, and defers to the current thread's SynchronizationContext to decide how to defer work. Task的等待实现是非常合理的,并且遵循当前线程的SynchronizationContext来决定如何推迟工作。 In the case that you're already in a WinForms or WPF message loop, then your deferred execution will come back on the same message loop (as if you used BeginInvoke() the "rest of your method"). 如果您已经在WinForms或WPF消息循环中,那么您的延迟执行将返回到同一消息循环(就像您使用BeginInvoke() “您的方法的其余部分”)。 If you await a Task and you're already on the .NET threadpool, then the "rest of your method" will resume on one of the threadpool threads (but not necessarily the same one exactly), since they were pooled to begin with and most likely you're happy to go with the first available pool thread. 如果你等待一个任务并且你已经在.NET线程池中,那么“你的方法的其余部分”将在其中一个线程池线程上恢复(但不一定完全相同),因为它们被合并开始并且您很可能很乐意使用第一个可用的池线程。


Caution about using Wait() methods 关于使用Wait()方法的注意事项

In your sample you used: var t = TaskEx.Run( () => Test().Wait() ); 在您使用的示例中: var t = TaskEx.Run( () => Test().Wait() );

What that does is: 这样做是:

  1. In the surrounding thread synchronously call TaskEx.Run(...) to execute a lambda on the thread pool. 在周围的线程中同步调用TaskEx.Run(...)在线程池上执行lambda。
  2. A thread pool thread is designated for the lambda, and it invokes your async method. 为lambda指定了一个线程池线程,它调用你的异步方法。
  3. The async method Test() is invoked from the lambda. 从lambda调用异步方法Test()。 Because the lambda was executing on the thread pool, any continuations inside Test() can run on any thread in the thread pool. 因为lambda正在线程池上执行,所以Test()中的任何延续都可以在线程池中的任何线程上运行。
  4. The lambda doesn't actually vacate that thread's stack because it had no awaits in it. lambda实际上并没有腾出那个线程的堆栈,因为它没有等待它。 The TPL's behavior in this case depends on if Test() actually finished before the Wait() call. 在这种情况下,TPL的行为取决于在Wait()调用之前Test()是否实际完成。 However, in this case, there's a real possibility that you will be blocking a thread pool thread while it waits for Test() to finish executing on a different thread. 但是,在这种情况下,您可能会在等待Test()完成在不同线程上执行时阻塞线程池线程。

That's the primary benefit of the 'await' operator is that it allows you to add code that executes later - but without blocking the original thread. 'await'运算符的主要好处是它允许您添加稍后执行的代码 - 但不会阻塞原始线程。 In the thread pool case, you can achieve better thread utilization. 在线程池的情况下,您可以实现更好的线程利用率。

Let me know if you have other questions about the Async CTP for VB or C#, I'd love to hear them :) 如果您对VB或C#的Async CTP有其他疑问,请告诉我,我很想听听他们:)

It's usually up to the method returning the Task to determine where it runs, if it's starting genuinely new work instead of just piggy-backing on something else. 这通常取决于返回Task以确定它运行的位置的方法,如果它正在开始真正的新工作而不仅仅是捎带其他东西。

In this case it doesn't look like you really want the Test() method to be async - at least, you're not using the fact that it's asynchronous. 在这种情况下,看起来你真的不希望Test()方法是异步的 - 至少,你没有使用它是异步的事实。 You're just starting stuff in a different thread... the Test() method could be entirely synchronous, and you could just use: 你只是在另一个线程中启动东西...... Test()方法可以完全同步,你可以使用:

Task task = TaskEx.Run(Test);
// Do stuff
t.Wait();

That doesn't require any of the async CTP goodness. 这不需要任何异步CTP优点。

There would be, if this wasn't a console application. 如果这不是一个控制台应用程序,那就会有。 For example, if you do this in a Windows Forms application, you could do: 例如,如果在Windows窗体应用程序中执行此操作,则可以执行以下操作:

// Added to a button click event, for example
public async void button1_Click(object sender, EventArgs e)
{
    // Do some stuff
    await Test();
    // Do some more stuff
}

However, there is no default SynchronizationContext in a console, so that won't work the way you'd expect. 但是,控制台中没有默认的SynchronizationContext ,因此无法按照您的预期运行。 In a console application, you need to explicitly grab the task and then wait at the end. 在控制台应用程序中,您需要显式获取任务,然后在最后等待。

If you're doing this in a UI thread in Windows Forms, WPF, or even a WCF service, there will be a valid SynchronizationContext that will be used to marshal back the results properly. 如果您在Windows窗体,WPF或甚至WCF服务的UI线程中执行此操作,则会有一个有效的SynchronizationContext,用于正确地封送结果。 In a console application, however, when control is "returned" at the await call, the program continues, and just exits immediately. 但是,在控制台应用程序中,当在await调用中“返回”控制时,程序将继续,并立即退出。 This tends to mess up everything, and produce unexpected behavior. 这往往会弄乱一切,并产生意想不到的行为。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM