C# 调用异步方法涉及的三个线程

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

我正在研究async,我遇到了以下无法解释的结果

下面的代码(可以复制/粘贴到Linqpad或类似代码中)给出了使用线程池中的三个线程的惊人结果

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,但是。