问题
Implementing a reusable adaptor type of library on top of Azure Document Db Client SDK.
The library can run anywhere, not only in an ASP.NET Core web service, but in a command line app, ASP.NET Web Api etc.
In this library all methods are async and they are simply layers of abstraction that makes it easier to use Document Db client api s. The only real async call - I/O request - is actually done on the lowest layer by the api s from the Document Db SDK. Any code above that I wrote is just in memory data transformations, conversions, no actual I/O call is involved but they are all async as well since the lowest layer Document Db api s are async.
Do I still need to use ConfigureAwait(false)
on all layers of upper level code in the stack, or is that enough to only call ConfigureAwait(False)
on the lowest layer of my own code that calls Document Db SDK s methods which make the real I/O call ?
回答1:
The continuations unlike a synchronous code which precedes an await
call, are executed in context of different call-stack calls. In particular they are scheduled for execution as separate delegates and there is no call-stack relationship between them. So specifying ConfigureAwait(false)
for very last continuation execution will take effect only for this specific continuation, other continuations will still execute using their individual scheduling configuration. That is, if your goal is to ensure not capturing a synchronization context by any continuation in your library (in order to prevent potential dead locks or any other reason) then you should configure all await
calls which have continuations with ConfigureAwait(false)
.
回答2:
ConfigureAwait(false)
is used to prevent execution on initial SynchronizationContext
. If you're working on library which doesn't need access to UI thread for instance (in case of WPF or WinForms) you should use ConfigureAwait(false)
on all levels. Otherwise SynchronizationContext
will be restored. Here is an example of simple WinForms application:
public partial class Form1 : Form
{
static readonly HttpClient _hcli = new HttpClient();
public Form1()
{
InitializeComponent();
}
private static string log;
private async void button1_Click(object sender, EventArgs e)
{
log = "";
await M3();
MessageBox.Show(log);
}
static async Task<string> M3()
{
LogBefore(nameof(M3));
var str = await M2();
LogAfter(nameof(M3));
return str;
}
static async Task<string> M2()
{
LogBefore(nameof(M2));
var str = await M1();
LogAfter(nameof(M2));
return str;
}
static async Task<string> M1()
{
LogBefore(nameof(M1));
var str = await _hcli.GetStringAsync("http://mtkachenko.me").ConfigureAwait(false);
LogAfter(nameof(M1));
return str;
}
static void LogBefore(string method)
{
log += $"before {method} {Thread.CurrentThread.ManagedThreadId} {SynchronizationContext.Current == null}, ";
}
static void LogAfter(string method)
{
log += $"after {method} {Thread.CurrentThread.ManagedThreadId} {SynchronizationContext.Current == null}, ";
}
}
Output:
before M3 1 False
before M2 1 False
before M1 1 False
after M1 12 True //sync.context skipped because of .ConfigureAwait(false)
after M2 1 False //sync.context restored
after M3 1 False //sync.context restored
来源:https://stackoverflow.com/questions/57054770/is-configureawaitfalse-required-on-all-levels-of-the-async-chain-when-no-real