在第一种方法中,编译器将围绕它生成“状态机”代码,并在任务完成后执行将返回到行return await service.GetAsync();。考虑下面的例子:
public async Task<Something> GetSomethingAsync()
{
var somethingService = new SomethingService();
// Here execution returns to the caller and returned back only when Task is completed.
Something value = await service.GetAsync();
DoSomething();
return value;
}
DoSomething(); 行只有在service.GetAsync 任务完成后才会执行。
第二种方法只是开始执行service.GetAsync并将对应的Task返回给调用者而不等待完成。
public Task<Something> GetSomethingAsync()
{
var somethingService = new SomethingService();
Task<Something> valueTask = service.GetAsync();
DoSomething();
return valueTask;
}
所以在上面的例子中DoSomething() 将在Task<Something> valueTask = service.GetAsync(); 行之后直接执行而不等待任务完成。
在另一个线程上执行async 方法取决于方法本身。
如果方法执行IO操作,那么另一个线程只会浪费线程,它什么也不做,只等待响应。我认为async - await 是IO 操作的完美方法。
如果方法GetAsync 包含例如Task.Run,则执行转到从线程池中获取的另一个线程。
下面是一个简短的例子,不是一个很好的例子,但它显示了一个试图解释的逻辑:
static async Task GetAsync()
{
for(int i = 0; i < 10; i++)
{
Console.WriteLine($"Iterate GetAsync: {i}");
await Task.Delay(500);
}
}
static Task GetSomethingAsync() => GetAsync();
static void Main(string[] args)
{
Task gettingSomethingTask = GetSomethingAsync();
Console.WriteLine("GetAsync Task returned");
Console.WriteLine("Start sleeping");
Thread.Sleep(3000);
Console.WriteLine("End sleeping");
Console.WriteLine("Before Task awaiting");
gettingSomethingTask.Wait();
Console.WriteLine("After Task awaited");
Console.ReadLine();
}
接下来是输出:
Iterate GetAsync: 0
GetAsync Task returned
Start sleeping
Iterate GetAsync: 1
Iterate GetAsync: 2
Iterate GetAsync: 3
Iterate GetAsync: 4
Iterate GetAsync: 5
End sleeping
Before Task awaiting
Iterate GetAsync: 6
Iterate GetAsync: 7
Iterate GetAsync: 8
Iterate GetAsync: 9
After Task awaited
正如您所见,GetAsync 的执行在调用它之后立即开始。
如果GetSomethingAsync() 将更改为:
static Task GetSomethingAsync() => new Task(async () => await GetAsync());
如果GetAsync 包裹在另一个任务中,那么GetAsync() 将根本不会执行,输出将是:
GetAsync Task returned
Start sleeping
End sleeping
Before Task awaiting
After Task awaited
当然,您需要删除行gettingSomethingTask.Wait();,因为那时应用程序只是等待甚至没有启动的任务。