直接等待和稍后等待的区别
本文关键字:等待 区别 | 更新日期: 2023-09-27 17:57:54
我找不到任何关于实际差异的信息。
所以我一直在做以下事情:
public async Task<int> DoSomethingAsync()
{
DoSomethingElse()
var result = await GetValueAsync();
var intValue = DoAnotherThing(result);
return intValue;
}
await
调用,然后直接使用它,但我最近看到一些帖子,人们正在做以下事情:
public async Task<int> DoSomethingAsync()
{
var task = GetValueAsync();
DoSomethingElse()
var result = await task;
var intValue = DoAnotherThing(result);
return intValue;
}
那么,这是否意味着方法GetValueAsync
开始执行它,然后我们稍后等待它的结果?或者这意味着它正在同步执行,但它仍然可以await
?
Damien_The_Unbeliever说得对,这取决于实现。
class Program
{
public static int DoAnotherThing(string value)
{
return int.Parse(value);
}
public static async Task<string> GetValueAsync()
{
await Task.Delay(5000);
return "12";
}
public static void DoSomethingElse()
{
Thread.Sleep(5000);
}
public static async Task<int> DoSomethingAsyncA()
{
DoSomethingElse();
var result = await GetValueAsync();
var intValue = DoAnotherThing(result);
return intValue;
}
public static async Task<int> DoSomethingAsyncB()
{
var task = GetValueAsync();
DoSomethingElse();
var result = await task;
var intValue = DoAnotherThing(result);
return intValue;
}
public static void Measure(Action act)
{
var sw = new Stopwatch();
sw.Start();
act();
sw.Stop();
Console.WriteLine(sw.Elapsed);
}
static void Main(string[] args)
{
Measure(() => DoSomethingAsyncA().Wait());
Measure(() => DoSomethingAsyncB().Wait());
Console.ReadLine();
}
}
在这种情况下,DoSomethingAsyncB
将花费约5秒,其中DoSomethingAsyncA
将花费约10秒。但是,如果GetValueAsync
返回的值计算是从其他地方开始的,那么就没有区别了。
更新我更正了答案中的错误。这是我机器的输出:
00:00:10:0161446
00:00:05:0032000
在此中
public async Task<int> DoSomethingAsync()
{
DoSomethingElse()
var result = await GetValueAsync();
var intValue = DoAnotherThing(result);
return intValue;
}
这意味着当您在不执行任何其他代码的情况下尽快等待GetValueAsync的结果时。
下面的代码表示:
public async Task<int> DoSomethingAsync()
{
var task = GetValueAsync();
DoSomethingElse()
var result = await task;
var intValue = DoAnotherThing(result);
return intValue;
}
在等待GetValueAsync完成时,您调用GetValueAsyncanddoSomethingElse,然后当它完成时,它继续执行DoAnotherThing
人们通常认为异步等待是由几个线程完成的。通常情况并非如此。除非启动其他线程,否则所有代码都由同一个线程执行。检查当前线程的ThreadId会显示这一点。
在一次采访中,Eric Lippert用餐馆的比喻解释了异步等待。在中间的某个位置搜索异步等待。
他解释说,如果厨师烤了一些面包,他可以等到面包烤好,或者开始做其他事情,然后再回来看看烤面包机是否烤好了。
在您的第二个DoSomethingAsync中也会发生同样的情况。将日期写入磁盘时,线程会将数据传递给磁盘写入程序。写入磁盘是一个相对缓慢的过程。线程不等待写入日期,而是开始做其他事情。当它完成其他操作时,线程将等待,直到磁盘写入完成对磁盘的写入。
只有当磁盘写入可以在线程执行其他操作时继续写入时,这才有用。因此,当涉及硬件时,您通常会看到异步函数,或者启动其他线程来完成某些工作的进程。如果异步函数必须进行一些耗时的计算,那么使函数异步是没有用的,除非它启动另一个线程来进行计算。即便如此,这是否应该在异步函数中完成,或者调用方是否应该自由决定是让自己的线程进行计算,还是启动另一个线程来进行计算,都是值得怀疑的。
另一篇帮助我理解异步等待的文章由Stephen Cleary撰写,他是本网站的伟大贡献者(再次感谢Stephen!)
在第一个例子中,在GetValueAsync()
返回结果之前,您不会执行代码。
在第二个步骤中,您将执行var task = GetValueAsync();
和var result = await task;
之间的代码,执行DoSomethingElse()
。
参见小示例:
public static Task<int> DoLongOperationAsync()
{
return Task.Run(() => { Thread.Sleep(5000); return 5; }); // 5 seconds
}
public static async Task TestMethod()
{
var result = await DoLongOperationAsync();
Console.WriteLine("Console");
Console.WriteLine(result);
}
public static async Task TestMethod2()
{
var result = DoLongOperationAsync();
Console.WriteLine("Console");
Console.WriteLine(await result);
}
static async DoSomething()
{
await TestMethod(); // here you see some text in console only after 5 seconds
await TestMethod2(); // here you see some text in console immediately and after 5 seconds you will see "5"
}
您只需将Task结果的要求推迟到第一次实际使用它的语句,这意味着当执行到达该语句时,任务可能已经异步设置了它的结果。