假设我有以下代码:public async void Run(){ TaskCompletionSource t = new TaskCompletionSource(); Prepare(t.Task); await Task.Delay(1000); t.SetResult(); Console.
假设我有这样的代码:
public async void Run()
{
TaskCompletionSource t = new TaskCompletionSource();
Prepare(t.Task);
await Task.Delay(1000);
t.SetResult();
Console.WriteLine("End");
}
public async void Prepare(Task task)
{
await Run(task, "A");
await Run(task, "B");
await Run(task, "C");
await Run(task, "D");
}
public async Task Run(Task requisite, string text)
{
await requisite;
Console.WriteLine(text);
}
时会发生什么 t.SetResult();
?这是多线程吗?控制台中项目的顺序有任何保证吗?如果我有一个 List<>
,并且该 Run
方法会改变它,我需要担心多线程吗?
当你写了一个简单的 await
语句时;
await requisite;
C# 编译器将您的方法移至单独的类,并将该“简单”转换 await
为等效的内容;
private Task requisite;
private int state = 0;
private void MoveNext()
{
switch (state)
{
case 0:
var awaiter = requisite.GetAwaiter();
if (!awaiter.IsCompleted)
{
state = 1;
awaiter.OnCompleted(MoveNext);
return;
}
goto case 1;
case 1:
// resume execution here when the task is complete
break;
}
}
如您所见,如果任务已完成,您的代码将同步继续。如果任务未完成,则会注册一个回调,以便稍后继续执行。
每个等待程序的具体实现可能完全不同。例如, Task.Yield
将返回一个永远不会完成的等待程序。强制继续在线程池上执行。
大多数任务类型以及示例中的所有任务都会在 .SetResult
调用时同步调用每个延续方法。
回到你的具体例子;
public async void Prepare(Task task)
{
await Run(task, "A");
await Run(task, "B");
await Run(task, "C");
await Run(task, "D");
}
第一次调用时, Run
任务未完成,因此 Run
将注册一个延续 Action
中的 await
第一个 Prepare
也将看到返回的任务未完成。尽管该方法 void
如此,但不会返回另一个未完成的任务。
当您在延迟后恢复并调用 时 t.SetResult();
将首先调用 Run
已注册的用于恢复 的延续方法 Run
方法完成时,而不是返回, .SetResult
将在未完成的任务上调用 ,从而立即调用 的延续 Prepare
。所有这些都将在同一线程中发生,在 t.SetResult
返回之前。
或 Run
中放置断点 Prepare
并检查调用堆栈,您会看到线程堆栈与您通常预期的相反。每个返回 async
在堆栈中 SetResult
调用