ConfigureAwait(false) vs setting sync context to null

后端 未结 2 1540
傲寒
傲寒 2021-02-08 05:07

I often see recommended for async library code, that we should use ConfigureAwait(false) on all async calls to avoid situations where the return of our call will be

相关标签:
2条回答
  • 2021-02-08 05:55

    Synchronization Context is similar to static variable and changing it without restoring before control leaves your method will lead to unexpected behavior.

    I don't believe you can safely set current thread's synchronization context inside library function that await anything as restoring context in the middle of compiler generated code is not really possible to my knowledge.

    Sample:

     async Task<int> MyLibraryMethodAsync()
     {
        SynchronizationContext.SetSynchronizationContext(....);
        await SomeInnerMethod(); // note that method returns at this point
    
        // maybe restore synchronization context here...
        return 42;
     }
    
     ...
     // code that uses library, runs on UI thread
    
     void async OnButtonClick(...)
     {
        // <-- context here is UI-context, code running on UI thread
        Task<int> doSomething = MyLibraryMethodAsync(); 
        // <-- context here is set by MyLibraryMethod - i.e. null, code running on UI thread
        var textFromFastService = await FastAsync();
        // <-- context here is set by MyLibraryMethod, code running on pool thread (non-UI)
        textBox7.Text = textFromFastService; // fails...
    
        var get42 = await doSomething;
    }
    
    0 讨论(0)
  • 2021-02-08 06:07

    I often see recommended for async library code, that we should use ConfigureAwait(false) on all async calls to avoid situations where the return of our call will be scheduled on a UI thread or a web request synchronization context causing issues with deadlocks among other things.

    I recommend ConfigureAwait(false) because it (correctly) notes that the calling context is not required. It also gives you a small performance benefit. While ConfigureAwait(false) can prevent deadlocks, that is not its intended purpose.

    It seems to me that a viable alternative is to simply set the current synchronization context to null at the top-level public-facing entry points of the library, and just forget about ConfigureAwait(false).

    Yes, that is an option. It won't completely avoid deadlocks, though, because await will attempt to resume on TaskScheduler.Current if there's no current SynchronizationContext.

    Also, it feels wrong to have a library replacing a framework-level component.

    But you can do this if you want. Just don't forget to set it back to its original value at the end.

    Oh, one other pitfall: there are APIs out there that will assume the current SyncCtx is what's provided for that framework. Some ASP.NET helper APIs are like that. So, if you call back end-user code, then that could be a problem. But in that case, you should explicitly document what context their callbacks are invoked in anyway.

    However, I don't see many instances of people taking or recommending this approach.

    It is slowly becoming more popular. Enough so that I've added an API for this in my AsyncEx library:

    using (SynchronizationContextSwitcher.NoContext())
    {
      ...
    }
    

    I haven't used this technique myself, though.

    Are there any potential problems with this approach (other than the possible insignificant performance hit of having the await post to the default synchronization context)?

    Actually, it's an insignificant performance gain.

    0 讨论(0)
提交回复
热议问题