[英]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
。 这并不少见。 两个主要场景是
WhenAny
或WhenAll
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.