繁体   English   中英

调用异步方法的两种方式。 C#

[英]Two ways of calling asynchronous method. C#

这两种方法有区别吗? 或者程序在这两种情况下运行类似? 如果有差异,你能说这些差异是什么。

第一种方法:

Task myTask = MyFunctionAsync();
await myTask;

第二种方法:

await MyFunctionAsync();

简短版本:“不是真的,至少不是以有趣的方式”

长版本:等待不限于Task / Task<T> ,因此可以(实际上微不足道的)创建可以编译的代码:

await MyFunctionAsync();

但不编译:

Task myTask = MyFunctionAsync();
await myTask;

仅仅是因为MyFunctionAsync()返回的东西不是任务 ValueTask<int>就足够了,但如果你愿意,你可以制作异国情调的等待对象。 但是:如果我们用var替换Task ,即

var myTask = MyFunctionAsync();
await myTask;

那么现在唯一的区别是,如果我们愿意,我们可以在代码的其他点引用myTask 这并不少见。 两个主要场景是

  • 结合对并发代码的多项检查,可能使用WhenAnyWhenAll
  • (通常在ValueTask[<T>]的情况下)检查 awaitable 是否同步完成,以避免同步情况下的 state 机器开销

它们实际上是相同的。 不同之处在于,第一种方法允许您在等待响应之前执行更多步骤。 因此,您可以以第一种方式同时启动许多任务,然后将它们与await Task.WhenAll(myListOfTasks)一起等待

例如:

var myTasks = myEmployees.Select(e => ProcessPayrollAsync(e));
await Task.WhenAll(myTasks);

如果你需要并发,我会使用第一种方法,如果它是一个简单的情况,我会使用第二种方法,因为它更短。

在这种特殊情况下,2 forms 的代码以类似的方式执行。 Homewer,考虑一下:

public async Task<int> CalculateResult(InputData data) {
    // This queues up the work on the threadpool.
    var expensiveResultTask = Task.Run(() => DoExpensiveCalculation(data));

    // Note that at this point, you can do some other work concurrently,
    // as CalculateResult() is still executing!

    // Execution of CalculateResult is yielded here!
    var result = await expensiveResultTask;

    return result;
}

正如上面代码中的注释所指出的,在任务正在运行和await调用之间,您可以执行任何其他并发代码。

有关更多信息,请阅读本文

真正帮助我理解 async-await 的是 Eric Lippert 在这次采访中描述的厨师类比。 在中间的某处搜索异步等待。

在这里,他描述了一位正在做早餐的厨师。 一旦他把水壶烧开来泡茶,他就不会闲着等水煮好了。 相反,他把面包放在烤面包机里,茶放在茶壶里,然后开始切西红柿:每当他必须等待另一台机器或其他厨师完成他的工作时,他不会无所事事,而是开始下一个任务,直到他需要先前任务之一的结果。

异步等待也是如此:每当另一个进程必须做某事时,您的线程只能等待另一个进程完成,而该线程可以环顾四周,看看它是否可以做其他事情。 当涉及另一个冗长的过程时,您通常会看到 async-await:将数据写入文件,从数据库或 Internet 查询数据。 每当您的线程必须执行此操作时,该线程可以命令其他进程执行某些操作,同时继续执行其他操作:

Task<string> taskReadFile = ReadMyFileAsync(...);

// not awaiting, we can do other things:
DoSomethingElse();

// now we need the result of the file:
string fetchedData = await taskReadFile;

那么会发生什么。 ReadMyFileAsync 是异步的,因此您知道在其内部某处,该方法正在等待。 事实上,如果您忘记等待,您的编译器会警告您。

一旦你的线程看到等待,它就知道需要等待的结果,所以它不能继续。 相反,它会向上调用堆栈继续处理(在我的示例中为 DoSomethingElse()),直到它看到等待。 它再次上升调用堆栈并继续处理等。

因此,实际上您的第一种方法和第二种方法之间没有真正的区别。 您可以将其与以下内容进行比较:

double x = Math.Sin(4.0)

相对

double a = 4.0;
double x = Math.Sin(a);

官方唯一的区别是在这些语句之后你仍然可以使用 a. 同样,您可以在等待之后使用来自任务的信息:

Task<MyData> myTask = FetchMyDataAsync(...);
MyData result = await myTask;

// if desired you can investigate myTask
if (result == null)
{
    // why is it null, did someone cancel my task?
    if (Task.IsCanceled)
    {
        Yell("Hey, who did cancel my task!");
    }
}

但大多数时候你对任务不感兴趣。 如果您在任务执行时没有其他事情可做,我会等待它:

MyData fetchedData = await FetchMyDataAsync(...)

暂无
暂无

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

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