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

发布于 2024-10-17 04:36:11 字数 424 浏览 8 评论 0原文

我正在评估异步 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
}

I'm evaluating the 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
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

時窥 2024-10-24 04:36:11

我是 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 之上,所以我们不想修补实际 System.Threading.Tasks.Task 在 mscorlib 中键入。相反,当 Playground API 发生冲突时,它们会被命名为 FooEx。

为什么我们将其中一些命名为 Run(...) 和一些 RunEx(...)?原因是我们发布 CTP 时尚未完成方法重载的重新设计。在我们当前的工作代码库中,我们实际上必须稍微调整 C# 方法重载规则,以便异步 Lambda 发生正确的事情 - 它可以返回 voidTask、或任务

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

因此,async void lambda 和 async Task lambda 都支持不带参数的 return; 。因此,需要对方法重载决策进行澄清,以决定选择哪一个。因此,同时拥有 Run(...) 和 RunEx(...) 的唯一原因是,我们可以确保在 PDC 2010 到来时为异步 CTP 的其他部分提供更高质量的支持。


如何思考异步方法/lambda

我不确定这是否是一个混淆点,但我想我会提到它 - 当你编写异步方法或异步 lambda 时,它可以呈现调用它的人的某些特征。这是基于两件事:

  • 您正在等待的类型
  • 以及可能的同步上下文(取决于上面)。await

的 CTP 设计和我们当前的内部设计都是非常基于模式的,因此 API 提供者可以帮助充实一个充满活力的模式。一系列你可以“等待”的事情。这可能会根据您等待的类型而有所不同,常见的类型是任务

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


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

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

其作用是:

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

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

如果您对 VB 或 C# 的异步 CTP 有其他疑问,请告诉我,我很乐意听听:)

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 :)

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

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. Instead, the playground APIs are named FooEx when they conflicted.

Why did we name some of them Run(...) and some of the RunEx(...)? The reason is because of redesigns in method overloading that we hadn't completed yet by the time we released the 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>.

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

So thus, both async void lambdas and async Task lambdas support 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.


How to think about async methods/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. 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. This can vary based on the type on which you're awaiting, and the common type for that is Task.

Task's await implementation is very reasonable, and defers to the current thread's SynchronizationContext to decide how to defer work. 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"). 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.


Caution about using Wait() methods

In your sample you used:
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.
  2. A thread pool thread is designated for the lambda, and it invokes your async method.
  3. The async method Test() is invoked from the lambda. Because the lambda was executing on the thread pool, any continuations inside Test() can run on any thread in the thread pool.
  4. The lambda doesn't actually vacate that thread's stack because it had no awaits in it. The TPL's behavior in this case depends on if Test() actually finished before the Wait() call. 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.

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. 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 :)

舞袖。长 2024-10-24 04:36:11

通常由返回 Task 的方法来确定它的运行位置,是否正在开始真正的新工作,而不是仅仅依靠其他东西。

在这种情况下,您似乎并不真的希望Test()方法是异步的 - 至少,您没有使用事实上它是异步的。您只是在不同的线程中开始工作... Test() 方法可以完全同步,您可以只使用:

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

这不需要任何异步 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.

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. You're just starting stuff in a different thread... the Test() method could be entirely synchronous, and you could just use:

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

That doesn't require any of the async CTP goodness.

堇年纸鸢 2024-10-24 04:36:11

如果这不是控制台应用程序的话,就会有。例如,如果您在 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
}

但是,控制台中没有默认的 SynchronizationContext,因此它不会按您期望的方式工作。在控制台应用程序中,您需要显式地获取任务,然后等待结束。

如果您在 Windows 窗体、WPF 甚至 WCF 服务的 UI 线程中执行此操作,则将有一个有效的 SynchronizationContext 用于正确封送结果。然而,在控制台应用程序中,当在 await 调用中“返回”控制权时,程序会继续执行,然后立即退出。这往往会搞乱一切,并产生意想不到的行为。

There would be, if this wasn't a console application. For example, if you do this in a Windows Forms application, you could do:

// 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. 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. In a console application, however, when control is "returned" at the await call, the program continues, and just exits immediately. This tends to mess up everything, and produce unexpected behavior.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文