使用 Task.Run 是异步等待的简写吗? [英] Is using Task.Run a short hand for async await?

查看:62
本文介绍了使用 Task.Run 是异步等待的简写吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Task.Run(fn1sync());
Task.Run(fn2sync());

以上代码在新线程中启动每个任务,并在将控制权返回给主线程的同时并行运行它们.

Above code starts each task in new thread, and runs them parallelly while returning control to main thread.

代替上面的,我可以更改函数以使用异步等待和写入的概念:

Instead of above, I can change the functions to use concept of async await and write:

var v1 = fn1Async()
var v2 = fn2Async()
...do some work..
Task.WaitAll(v1, v2)

现在.NET会自动处理是使用单线程还是多线程,并且并行运行并立即将控制权交给主线程.

Now .NET will auto handle whether to use single thread or multiple threads and also run them in parallel and immediately pass control to main thread.

我什么时候需要使用这些方法中的任何一种?

When do I need to use either of these approaches?

推荐答案

我不确定您的问题的标题是否与您在正文中提出的实际问题完全匹配,因此我将使用正文:

I'm not sure the title of your question totally matches the actual question you're asking in the body, so I'll go with the body:

您的两个示例之间至少有一个非常大的区别.有了这个(选项 1):

There's at least one really big difference between your two examples. With this (Option 1):

Task.Run(fn1sync());
Task.Run(fn2sync());

您无法等待两个任务完成.它们将并行运行(可能 - 但不能保证 - 在不同的线程中).它们都将立即开始并在完成时结束.之后的任何代码也将立即执行,无需等待任务完成.

you have no way of waiting for both tasks to finish. They will run in parallel (PROBABLY - but not guaranteed - in different threads). They will both begin immediately and finish when they finish. Any code after that will also execute immediately, without waiting for the tasks to finish.

有了这个(选项 2):

With this (Option 2):

var v1 = fn1Async()
var v2 = fn2Async()
...do some work..
Task.WaitAll(v1, v2)

您坚持执行任务,然后在做一些工作"之后,您明确地等待两者完成后再继续.最后一行之后的任何内容都不会执行,直到两者都完成.

You're holding onto the tasks and then after "do some work", you're explicitly waiting for both to finish before continuing. Nothing after the last line will execute until both are finished.

这个问题变得更有趣了——也许这就是你想要的?- 如果您以这种方式重写选项 1(我们称之为选项 3):

The question becomes more interesting - and maybe this is what you were getting at? - if you re-write option 1 this way (let's call it Option 3):

var v1 = Task.Run(fn1sync());
var v2 = Task.Run(fn2sync());
...do some work...
Task.WaitAll(v1, v2);

选项 2 和选项 3 是否相同?答案仍然是否定的.

Are Option 2 and Option 3 identical? The answer is still no.

当您调用异步方法/委托/等时.不等待它,这就是选项 2 所做的,委托是否在调用分支可以继续之前实际完成取决于委托是否调用任何真正的异步代码.例如,您可以编写此方法:

When you call an async method/delegate/etc. without awaiting it, which is what option 2 does, whether the delegate actually finishes before the calling branch can continue depends on whether the delegate invokes any truly async code. For example you could write this method:

public Task fn1sync()
{
    Console.Write("Task complete!");
    return Task.CompletedTask;
}

Console.Write 行将始终在做一些工作"之前执行.为什么?因为如果您从字面上考虑返回对象 Task,那么 fn1sync 根据定义不能在写入之前将 Task 返回到您的调用代码到控制台(这是一个同步操作).替换Console.Write"一些非常缓慢,甚至可能阻塞的事情,你已经失去了异步的好处.

The Console.Write line will always execute before "do some work". Why? Because if you think of the return object Task literally, then fn1sync by definition can't be returning a Task to your calling code before it writes to the console (which is a synchronous operation). Replace "Console.Write" with something really slow, or maybe even blocking, and you've lost the benefits of async.

另一方面,当您使用 Task.Run 时,您保证控制流将立即返回到您的下一行代码,无论委托执行需要多长时间,无论是否委托实际上包含任何异步代码.因此,您可以确保您的调用线程不会因委托中的任何内容而变慢.事实上,当您必须调用同步代码时,使用 Task.Run 是一个好主意,而您有任何理由相信它会变慢 - 遗留 I/O、数据库、本机代码等.

When you use Task.Run, on the other hand, you guarantee that the control flow will return to your next line of code immediately, no matter how long the delegate takes to execute and regardless whether the delegate actually contains any async code. You thus ensure that your calling thread won't get slowed down by whatever is in the delegate. In fact it's a great idea to use Task.Run when you have to invoke synchronous code that you have any reason to believe is going to be slow - legacy I/O, databases, native code, etc.

调用 async 方法而不等待它或将其包装在 Task.Run(选项 2)中的用例可能不太明显,但它们确实存在.这是一个火与遗忘"只要您知道您正在调用的代码是行为良好的异步代码(即它不会阻塞您的线程),这种方法就可以了.例如,如果选项 2 中的 fn1Async 如下所示:

The use cases for calling an async method without awaiting it or wrapping it in Task.Run (Option 2) are perhaps less obvious but they do exist. It's a "fire and forget" approach that's fine as long as you know the code you're calling is well behaved async code (i.e. it's not going to block your thread). For example if fn1Async in Option 2 looked like this:

public Task fn1sync()
{
    return Task.Run(()=>
    {
         Console.Write("Task complete!");
    });
}

那么选项 2 和选项 3 将完全等效且同样有效.

Then Options 2 and 3 would be fully equivalent and equally valid.

更新:回答你的问题,你能告诉我为什么主线程中的 await 导致它阻塞等待结果,而函数中的 await 导致控制传递给主线程吗?"

Update: To answer your question, "Can you tell me why does await in main thread cause it to block waiting for result, whereas await inside a function results in control passing to main thread?"

我同意您可能希望找到有关 async/await/Task 的更全面的入门的一些评论.但这就是说,这完全是关于分支以及 await 关键字在编译时如何解释.

I agree with some of the comments that you might want to find a more comprehensive primer on async/await/Task. But that said, it's all about branching and how the await keyword is interpreted when it's compiled.

当你写作时:

async Task MyMethodAsync()
{
    Console.Write("A");
    int result = await AnotherMethodAsync();
    Console.Write("B");
    return;
}

这基本上被转换为:

Task MyMethodAsync()
{
    Console.Write("A");
    Task<int> task1 = AnotherMethodAsync();  // *should* return immediately if well behaved
    Task task2 = task1.ContinueWith((Task<int> t) => 
    {
        // t is same as task1 (unsure if they're identical instances but definitely equivalent)
        int result = t.Value;
        Console.Write("B");     ​
        ​return;
   ​ }); // returns immediately
    return task2; 
}

所有之前方法中第一个awaited 语句同步执行 - 在调用者的线程上.但是所有之后的第一个等待的语句都变成了一个回调,只有在第一个等待的语句 - task1 - 完成后才会执行.

Everything before the first awaited statement in a method executes synchronously - on the caller's thread. But everything after the first awaited statement becomes a callback that gets executed only after the first awaited statement - task1 - is finished.

重要的是,ContinueWith 返回它自己的 Task - task2,这就是返回给 MyMethodAsync,允许他们根据自己的选择await."Console.Write("B") 行保证在 task1 完成之前不会被执行.但是,当它相对于调用 MyMethodAsync 之后的代码执行时完全取决于该调用是否被awaited.

Importantly, ContinueWith returns its own Task - task2 and THAT is what is is returned to the caller of MyMethodAsync, allowing them to await it or not as they choose. The line "Console.Write("B") is guaranteed not to get executed until task1 is complete. However, when it gets executed relative to the code that follows the call to MyMethodAsync depends entirely on whether that call is awaited.

因此:

async Task Main()
{
     await MyMethodAsync();
     Console.Write("C");
}

变成:

Task Main()
{
     return MyMethodAsync().ContinueWith(t => 
     {
          // Doesn't get executed until MyMethodAsync is done
          Console.Write("C");
     });
}

因此保证输出为:ABC

and the output is thus guaranteed to be: ABC

然而:

void Main()
{
     Task t = MyMethodAsync(); // Returns after "A" is written

     // Gets executed as soon as MyMethodAsync
     // branches to `await`ed code, returning the `Task`
     // May or may not be before "B"
     Console.Write("C");
}

按字面执行,t 在A"之后以不完整状态返回.但在C"之前.因此保证输出以A"开始.但无论是B"或C"如果不知道 AnotherMethodAsync 正在做什么,将无法预测.

executes literally, with t getting returned in an incomplete state, after "A" but before "C". The output is thus guaranteed to start with "A" but whether "B" or "C" will come next will not be predictable without knowing what AnotherMethodAsync is doing.

这篇关于使用 Task.Run 是异步等待的简写吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆