即使被调用方法在内部执行ConfigureAwait(false),ConfigureAwait(true)是否总是返回到orignial线程?

时间:2018-05-31 03:40:10

标签: c# asynchronous async-await configureawait

我期待在1.2位回到第1号线,但我没有。有没有办法在进行异步调用后返回UI线程?感谢

另外我无法使顶级方法异步。不确定异步是否会解决这个问题,但我现在没有那个选择。

class Program
{
    static void Main(string[] args)
    {
        ComputeThenUpdateUI().Wait();
    } 
    static async Task ComputeThenUpdateUI()
    {
        Console.WriteLine($"1.1 {Thread.CurrentThread.ManagedThreadId}");
        await ExpensiveComputingNonUI().ConfigureAwait(true);
        Console.WriteLine($"1.2 {Thread.CurrentThread.ManagedThreadId}");
    } 
    static async Task ExpensiveComputingNonUI()
    {
        Console.WriteLine($"2.1 {Thread.CurrentThread.ManagedThreadId}");
        await Task.Delay(3000).ConfigureAwait(false);
        Console.WriteLine($"2.2 {Thread.CurrentThread.ManagedThreadId}");
        await Task.Delay(3000).ConfigureAwait(false);
        Console.WriteLine($"2.3 {Thread.CurrentThread.ManagedThreadId}");
    }
}
 
 
Output:
1.1 1
2.1 1
2.2 4
2.3 4
1.2 4

2 个答案:

答案 0 :(得分:3)

Re:public static async Task Main(string[] args) { // Can await asynchronous tasks here. } 是否意味着await完成后流将返回到同一个线程?

不,在大多数情况下,没有保证。 Stephen Cleary有一个明确的答案:

  

这个“上下文”是SynchronizationContext.Current,除非它是null,在这种情况下它是TaskScheduler.Current。 (如果当前没有正在运行的任务,则TaskScheduler.Current与TaskScheduler.Default(线程池任务调度程序)相同。)

     

重要的是要注意SynchronizationContext或TaskScheduler并不一定意味着特定的线程。 UI SynchronizationContext将为UI线程安排工作;但是ASP.NET SynchronizationContext不会为特定线程安排工作。

即。仅从例如UI线程进行的调用。 WPF或Windows窗体保证返回到同一个线程。如果你真的需要返回同一个帖子,那么你需要做customization to ensure the continuation is scheduled on the original thread

除非这是必要的(例如UI线程),否则返回同一个线程通常是不受欢迎的,因为这会降低性能(如果存在争用线程)和lead to deadlocks

Re:无法使顶级主要方法异步

现在可以在C#7.1

中使用
input(type='hidden', name='_csrf', value=_csrf)

答案 1 :(得分:1)

ConfigureAwait是您编写的每个方法中的本地决定。 ConfigureAwait(true)是一种表示您希望尝试返回首次输入方法时运行方法的方法。

首先,这意味着如果你不知道你最初在中运行的是什么上下文,那么尝试回到相同的上下文并没有太大的意义。如果可以在各种上下文中调用,则不应尝试执行与上下文相关的任何操作。这就是为什么如果你正在创作一个库,使用ConfigureAwait(false)通常是个好建议。您不会知道调用代码的上下文。

其次,这意味着没有“全球”背景可以回归。如果您的调用者(或者其中任何一个调用者等,在链的上方)已经将自己与他们的原始调用上下文分开,那么就没有办法(通过这个机制)来回到那个背景。

如果要切换到特定的上下文(例如UI线程)并且不能保证是您的调用上下文,则需要使用不同的特定于上下文的机制,例如Dispatcher.Invoke()或适合您环境的任何内容。