为什么同步上下文无法等待?

时间:2017-05-19 10:40:51

标签: c# asynchronous async-await

This answer

  

默认情况下,await运算符将捕获当前" context"并使用它来恢复异步方法。

我正在我的控制台应用中尝试此代码:

static void Main(string[] args)
{
    Test().Wait();
}

private static async Task Test()
{
    var context = new SynchronizationContext();
    SynchronizationContext.SetSynchronizationContext(context);
    Console.WriteLine("Thread before: " + Thread.CurrentThread.ManagedThreadId);
    Console.WriteLine(await GetResultAsync());
    Console.WriteLine("Thread after: " + Thread.CurrentThread.ManagedThreadId);
}

private static async Task<string> GetResultAsync()
{
    return await Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Thread inside: " + Thread.CurrentThread.ManagedThreadId);
        return "Hello stackoverflow!";
    });
}

...并解决这个问题:

Thread before: 1
Thread inside: 3
Hello stackoverflow!
Thread after: 3

为什么呢?如果我想在等待之后使用相同的线程,我还应该如何设置同步上下文?

2 个答案:

答案 0 :(得分:4)

  

为什么?

new SynchronizationContext() by conventionnull SynchronizationContext相同。两者都没有&#34;没有SyncCtx&#34;和&#34;默认的SyncCtx&#34;只是将工作排队到线程池。

SynchronizationContext与特定线程之间没有1:1的关系。例如:

  • WinForms UI SynchronizationContext将工作排队到单个UI线程。 AFAIK,WinForms SynchronizationContext是一个单例,因此在这种情况下有一个1:1映射
  • WPF SynchronizationContext会将工作排到其Dispatcher。上次我检查时,WPF将为每个顶级窗口创建一个新的实例,即使它们都使用相同的线程。所以有一个N:1映射。
  • 线程池(默认/ nullSynchronizationContext可以将工作排入任何线程池线程。如果您没有创建默认的SynchronizationContext实例,则会有1:N映射。
  

如果我想在等待之后使用相同的线程,我还应该如何设置同步上下文?

您需要使用自定义SynchronizationContext。我建议使用我的AsyncContext or AsyncContextThread types,因为这不是简单的代码。

答案 1 :(得分:1)

我不是这方面的专家,我刚读了一些教程。

await之后的代码将作为具有捕获的同步上下文的任务继续运行。您提供了使用new SynchronizationContext()来执行该代码的ThreadPool

源代码:link

查看Send类中的SynchronizationContext方法:

public virtual void Post(SendOrPostCallback d, Object state)
{
    ThreadPool.QueueUserWorkItem(new WaitCallback(d), state);
}

如您所见,继续不会在main线程上运行。它使用ThreadPool

完成GetResultAsync()后,thread 3可以免费使用,ThreadPool.QueueUserWorkItem中的SynchronizationContext会立即重复使用{。}}。

因此,您需要为控制台应用程序创建自己的同步上下文。

没看过,但也许这个link会有所帮助。