C# 调用异步方法涉及的三个线程
我正在研究async,我遇到了以下无法解释的结果 下面的代码(可以复制/粘贴到Linqpad或类似代码中)给出了使用线程池中的三个线程的惊人结果C# 调用异步方法涉及的三个线程,c#,.net,async-await,C#,.net,Async Await,我正在研究async,我遇到了以下无法解释的结果 下面的代码(可以复制/粘贴到Linqpad或类似代码中)给出了使用线程池中的三个线程的惊人结果 void Main() { Wait(); } public async void Wait() { Print ("Wait() called. Calling GetAnswer()"); var t = await GetAnswerAsync3(); Print("Result of Wait(): " + t
void Main()
{
Wait();
}
public async void Wait()
{
Print ("Wait() called. Calling GetAnswer()");
var t = await GetAnswerAsync3();
Print("Result of Wait(): " + t);
}
public Task<bool> GetAnswerAsync3()
{
return Task.Run(() => {
// Thread.Sleep(1000);
Print("GetAnswerAsync3() called");
return true;
});
}
public void Print(string message)
{
Console.WriteLine ("Thread: " + Thread.CurrentThread.ManagedThreadId + " - " + message);
}
显示涉及三个线程
现在。如果在GetAnswerAsync3
返回的任务返回之前添加Thread.Sleep(1000)
,结果现在只有两个线程在运行!这可能是因为线程池重新使用了线程
为什么这里有三个不同的线程在运行?一个
async
方法被分解成若干工作,在这种情况下,这些工作都被调度到线程池中<代码>等待指定异步方法可能“中断”的位置
第一个线程是主线程,它不是线程池的一部分
void Main()
{
Wait();
}
public async void Wait()
{
Print ("Wait() called. Calling GetAnswer()");
var t = await GetAnswerAsync3();
Print("Result of Wait(): " + t);
}
public Task<bool> GetAnswerAsync3()
{
return Task.Run(() => {
// Thread.Sleep(1000);
Print("GetAnswerAsync3() called");
return true;
});
}
public void Print(string message)
{
Console.WriteLine ("Thread: " + Thread.CurrentThread.ManagedThreadId + " - " + message);
}
第二个线程用于执行传递给任务的委托。运行。这可以是线程池中的任何线程
void Main()
{
Wait();
}
public async void Wait()
{
Print ("Wait() called. Calling GetAnswer()");
var t = await GetAnswerAsync3();
Print("Result of Wait(): " + t);
}
public Task<bool> GetAnswerAsync3()
{
return Task.Run(() => {
// Thread.Sleep(1000);
Print("GetAnswerAsync3() called");
return true;
});
}
public void Print(string message)
{
Console.WriteLine ("Thread: " + Thread.CurrentThread.ManagedThreadId + " - " + message);
}
第三个线程用于在其Wait
之后拾取Wait
的执行。这也可以是线程池中的任何线程
void Main()
{
Wait();
}
public async void Wait()
{
Print ("Wait() called. Calling GetAnswer()");
var t = await GetAnswerAsync3();
Print("Result of Wait(): " + t);
}
public Task<bool> GetAnswerAsync3()
{
return Task.Run(() => {
// Thread.Sleep(1000);
Print("GetAnswerAsync3() called");
return true;
});
}
public void Print(string message)
{
Console.WriteLine ("Thread: " + Thread.CurrentThread.ManagedThreadId + " - " + message);
}
从概念上讲,就是这样。事实上,这有点复杂:
我认为用线程解释更容易。先睡。在这种情况下,Wait
已被其Wait
在任务完成之前完全挂起。Run
完成。当Task.Run
完成时,它执行其延续,并且async
方法的延续已设置了ExecuteSynchronously
set()。因此,运行thread.Sleep
的同一个线程实际上继续执行Wait
,而没有屈服
您的原始代码有一个竞争条件:任务.Run
非常短,因此它将很快完成。您看到的是Wait
检查任务
,发现任务尚未完成,并挂起Wait
方法。同时,Task.Run
完成并将Wait
的剩余部分安排到线程池中
void Main()
{
Wait();
}
public async void Wait()
{
Print ("Wait() called. Calling GetAnswer()");
var t = await GetAnswerAsync3();
Print("Result of Wait(): " + t);
}
public Task<bool> GetAnswerAsync3()
{
return Task.Run(() => {
// Thread.Sleep(1000);
Print("GetAnswerAsync3() called");
return true;
});
}
public void Print(string message)
{
Console.WriteLine ("Thread: " + Thread.CurrentThread.ManagedThreadId + " - " + message);
}
如果您想了解更多有关await
如何处理上下文的信息,我推荐我的。你通常不必担心这样的细节。这很有道理。添加Thread.Sleep()
时,它为什么会改变?如果没有Sleep
,则存在一种争用条件,即第二个线程在将延续调度到第三个线程时仍被视为工作繁忙。使用Sleep
,当任务完成时,该方法已经挂起,因此(作为优化),第二个线程将直接执行延续。如果我在Sleep
就绪的情况下,将ConfigureAwait(false)
添加到GetAnswerAsync()
,为什么不再有三个线程?ConfigureAwait(false)
意味着您不关心当前上下文是否被await
捕获并用于继续该方法。这并不意味着“强制在线程池上调度,而不是直接执行”。我在我的-再次介绍,请阅读。默认情况下,await
将捕获当前的SynchronizationContext
(除非它是null
,在这种情况下它使用当前的TaskScheduler
),并且它将使用该上下文来恢复async
方法。因此,如果这是一个UI应用程序,它将返回到UI线程。控制台应用程序没有SyncCtx,但是。