【问题标题】:Why sync context is not working for await?为什么同步上下文不适用于等待?
【发布时间】:2017-05-19 20:12:03
【问题描述】:

This answer

默认情况下,await 运算符将捕获当前的“上下文”并使用它来恢复异步方法。

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

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

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

【问题讨论】:

  • 他们在这里明确使用术语上下文,因为并非所有上下文都绑定到单个线程。在控制台应用程序中,没有内在的理由支持一个线程优先于其他线程(与具有 UI 线程的 UI 框架不同)。

标签: c# asynchronous async-await


【解决方案1】:

为什么?

new SynchronizationContext() by conventionnull SynchronizationContext 相同。 “no SyncCtx”和“default SyncCtx”都只是将工作排队到线程池中。

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

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

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

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

【讨论】:

  • 实际上,在 WPF 中,SynchronizationContext.Currentawait 之后的任务异步完成不同。这是因为DispatcherDispatcherSynchronizationContext 的唯一实例上执行每个回调。与ConfigureAwaitcontinueOnCapturedContext 论点存在争议,IMO。
  • 奇怪!从几年前开始,这种行为已经发生了变化。
【解决方案2】:

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

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

源码:link

查看SynchronizationContext类中的Send方法:

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

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

GetResultAsync() 完成后,thread 3 可以免费使用,并立即被ThreadPool.QueueUserWorkItemSynchronizationContext 中重用。

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

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

【讨论】:

    猜你喜欢
    • 2020-04-01
    • 1970-01-01
    • 2019-05-25
    • 1970-01-01
    • 2014-10-23
    • 1970-01-01
    • 2016-11-20
    • 2018-06-22
    • 1970-01-01
    相关资源
    最近更新 更多