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

时间:2016-06-12 11:47:17

标签: c# async-await task-parallel-library

我正在玩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");
}

我得到了以下输出:

A)true / true

var continueOnCapturedContext1 = true;
var continueOnCapturedContext2 = true;
  

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;
  

1)11 23 23 22 22

     

2)11 19 19 19 19

     

3)11 10 10 10 10

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

C)false / true

var continueOnCapturedContext1 = false;
var continueOnCapturedContext2 = true;
  

1) 11 14 14 06 06

     

2)11 20 20 20 20

     

3) 11 17 17 08 08

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

看来,如果以前调用过,那么ConfigureAwait(true / false)对后续等待的调用没有影响,对吗?

1 个答案:

答案 0 :(得分: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);
    }
}

示例输出:

1
1
5

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