ConfigureAwait是否仅影响非线程池线程?

Jan*_*Net 3 c# task-parallel-library async-await

我正在玩一点,ConfigureAwait因为我想了解它是如何工作的.

因此,我编写了以下小型控制台应用程序(实际上在LINQPad中运行):

void Main()
{
    // Use dispatcher to execute the code on STA thread
    Dispatcher.CurrentDispatcher.Invoke(() => Run());
}

private async static Task Run()
{
    var continueOnCapturedContext1 = true;
    var continueOnCapturedContext2 = true;

    PrintThreadID();
    await Task.Run(() => PrintThreadID()).ConfigureAwait(continueOnCapturedContext1);
    PrintThreadID();
    await Task.Run(() => PrintThreadID()).ConfigureAwait(continueOnCapturedContext2);   
    PrintThreadID();
}

private static void PrintThreadID()
{
    Console.Write(Thread.CurrentThread.ManagedThreadId.ToString("00") + "\t");
}
Run Code Online (Sandbox Code Playgroud)

我得到以下输出:

A)真/真

var continueOnCapturedContext1 = true;
var continueOnCapturedContext2 = true;
Run Code Online (Sandbox Code Playgroud)

1)11 19 11 07 11

2)11 09 11 12 11

3)11 06 11 06 11

预期:捕获调度程序线程(11)并在不同或相同的线程池线程上执行等待的任务.

B)错误/错误

var continueOnCapturedContext1 = false;
var continueOnCapturedContext2 = false;
Run Code Online (Sandbox Code Playgroud)

1)11 23 23 22 22

2)11 19 19 19 19

3)11 10 10 10 10

还期望:未捕获SynchronizationContext,因此后续的等待和不可等的代码在线程池线程上运行(通常是相同的).

C)假/真

var continueOnCapturedContext1 = false;
var continueOnCapturedContext2 = true;
Run Code Online (Sandbox Code Playgroud)

1)11 14 14 06 06

2)11 20 20 20 20

3)11 17 17 08 08

输出1和3的结果很奇怪.使用选项"继续捕获上下文"执行2. awaitbale任务,所以我希望它在与调用它的代码相同的线程上运行.

似乎,如果以前调用它,那么ConfigureAwait(true/false)对后续等待的调用没有影响,对吧?

Jon*_*eet 11

使用选项"继续捕获上下文"执行2. awaitbale任务,所以我希望它在与调用它的代码相同的线程上运行.

这假定"context == thread",但它没有.使用线程池的同步上下文将在线程池中的任何线程上恢复.现在,如果你没有捕获同步上下文,你仍然会在"线程池中的一个线程"结束.

所以,是的,如果你已经在一个线程池线程上,那么你是否捕获同步上下文并不重要......延续仍然会在一个线程池线程上结束.但值得指出的是,拥有一个具有多个线程的不同同步上下文是完全合理的,并且捕获同步上下文将返回到其中一个线程,但不一定是同一个线程.

看来,ConfigureAwait(true/false)如果它之前已被调用过,对后续等待的通话没有任何影响,对吧?

不完全的.如果任务需要使用延续,那将是这种情况.如果您调用的第一个任务ConfigureAwait已经完成,代码将继续同步执行 - 所以在这一点上,第二个任务ConfigureAwait很重要.

例:

using System;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;

class Test
{    
    static void Main()
    {
        var form = new Form();
        form.Load += async (sender, args) =>
        {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            await Task.FromResult(10).ConfigureAwait(false);
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            await Task.Delay(1000).ConfigureAwait(false);
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        };
        Application.Run(form);
    }
}
Run Code Online (Sandbox Code Playgroud)

样本输出:

1
1
5
Run Code Online (Sandbox Code Playgroud)

所以第二个Console.WriteLine显示代码仍然在同一个线程上运行,尽管是ConfigureAwait(false),因为Task.FromResult返回已经完成的任务.