C# 与ConfigureAwait'异步/等待;s continueOnCapturedContext参数和异步继续的SynchronizationContext
我想先把代码放在前面,然后解释情况,并基于此提出我的问题:C# 与ConfigureAwait'异步/等待;s continueOnCapturedContext参数和异步继续的SynchronizationContext,c#,asynchronous,task-parallel-library,async-await,synchronizationcontext,C#,Asynchronous,Task Parallel Library,Async Await,Synchronizationcontext,我想先把代码放在前面,然后解释情况,并基于此提出我的问题: public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private async void Button_Click_2(object sender, RoutedEventArgs e) { var result = await GetValuesA
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private async void Button_Click_2(object sender, RoutedEventArgs e) {
var result = await GetValuesAsync();
Foo.Text += result;
}
public async Task<string> GetValuesAsync() {
using (var httpClient = new HttpClient()) {
var response = await httpClient
.GetAsync("http://www.google.com")
.ConfigureAwait(continueOnCapturedContext: false);
// This is the continuation for the httpClient.GetAsync method.
// We shouldn't get back to sync context here
// Cuz the continueOnCapturedContext is set to *false*
// for the Task which is returned from httpClient.GetAsync method
var html = await GetStringAsync();
// This is the continuation for the GetStringAsync method.
// Should I get back to sync context here?
// Cuz the continueOnCapturedContext is set to *true*
// for the Task which is returned from GetStringAsync
// However, GetStringAsync may be executed in another thread
// which has no knowledge for the sync context
// because the continueOnCapturedContext is set to *false*
// for the Task which is returned from httpClient.GetAsync method.
// But, on the other hand, GetStringAsync method also has a
// chance to be executed in the UI thread but we shouldn't be
// relying on that.
html += "Hey...";
Foo.Text = html;
return html;
}
}
public async Task<string> GetStringAsync() {
await Task.Delay(1000);
return "Done...";
}
}
公共部分类主窗口:窗口{
公共主窗口(){
初始化组件();
}
专用异步无效按钮\u单击\u 2(对象发送方,路由目标){
var result=await GetValuesAsync();
Foo.Text+=结果;
}
公共异步任务GetValuesAsync(){
使用(var httpClient=new httpClient()){
var response=wait-httpClient
.GetAsync(“http://www.google.com")
.ConfigureAwait(continueOnCapturedContext:false);
//这是httpClient.GetAsync方法的延续。
//我们不应该回到同步上下文
//因为continueOnCapturedContext被设置为*false*
//对于从httpClient.GetAsync方法返回的任务
var html=await GetStringAsync();
//这是GetStringAsync方法的延续。
//我应该回到同步上下文吗?
//因为continueOnCapturedContext设置为*true*
//对于从GetStringAsync返回的任务
//但是,GetStringAsync可以在另一个线程中执行
//它不知道同步上下文
//因为continueOnCapturedContext设置为*false*
//对于从httpClient.GetAsync方法返回的任务。
//但是,另一方面,GetStringAsync方法也有一个
//有可能在UI线程中执行,但我们不应该
//靠这个。
html+=“嘿…”;
Text=html;
返回html;
}
}
公共异步任务GetStringAsync(){
等待任务。延迟(1000);
返回“完成…”;
}
}
这是一个在.NET4.5上运行的相当简单的WPF示例,可能没有多大意义,但这应该可以帮助我解释我的情况
我在屏幕上有一个按钮,它有一个异步点击事件。当您查看GetValuesAsync
代码时,您将看到wait
关键字的用法两次。第一次使用时,我将任务的continueOnCapturedContext
参数设置为false。因此,这表明我不一定希望在SynchronizationContext.Current
中执行我的延续。到目前为止还不错
在第二次使用await
时(使用GetStringAsync
方法),我没有调用ConfigureAwait
方法。因此,我基本上表示我希望回到当前的同步上下文,继续使用GetStringAsync
方法。如您所见,我尝试在continuation中设置TextBlock.Text
(属于UI线程)属性
当我运行应用程序并单击按钮时,会出现异常,并显示以下消息:
调用线程无法访问此对象,因为另一个
线程拥有它
起初,这对我来说毫无意义,我认为我发现了一个bug,但后来,我意识到GetStringAsync
可能会在另一个线程中执行(很可能)它与UI线程不同,并且不知道同步上下文,因为从httpClient.GetAsync
方法返回的任务的continueOnCapturedContext
设置为false
这里是这样吗?另外,在这种情况下,是否有可能将GetStringAsync
方法发回UI线程,因为httpClient.GetAsync方法延续可能在UI线程内执行
我在代码中也有一些注释。鉴于我的问题和代码中的注释,我是否遗漏了什么?当您调用ConfigureAwait(false)
时,该方法的其余部分将在线程池线程上执行,除非您正在等待的任务已经完成
由于GetAsync
几乎肯定会异步运行,因此我希望GetStringAsync
在线程池线程上运行
public async Task<string> GetValuesAsync() {
using (var httpClient = new HttpClient()) {
var response = await httpClient
.GetAsync("http://www.google.com")
.ConfigureAwait(continueOnCapturedContext: false);
// And now we're on the thread pool thread.
// This "await" will capture the current SynchronizationContext...
var html = await GetStringAsync();
// ... and resume it here.
// But it's not the UI SynchronizationContext.
// It's the ThreadPool SynchronizationContext.
// So we're back on a thread pool thread here.
// So this will raise an exception.
html += "Hey...";
Foo.Text = html;
return html;
}
}
公共异步任务GetValuesAsync(){
使用(var httpClient=new httpClient()){
var response=wait-httpClient
.GetAsync(“http://www.google.com")
.ConfigureAwait(continueOnCapturedContext:false);
//现在我们在线程池线程上。
//此“等待”将捕获当前SynchronizationContext。。。
var html=await GetStringAsync();
//…然后在这里继续。
//但这不是UI SynchronizationContext。
//这是线程池同步上下文。
//所以我们回到了线程池线程。
//因此,这将引发一个例外。
html+=“嘿…”;
Text=html;
返回html;
}
}
此外,在这种情况下,GetStringAsync方法是否有可能被发回UI线程,因为httpClient.GetAsync方法延续可能在UI线程内执行
在UI线程上运行GetStringAsync
的唯一方法是GetAsync
在实际执行wait
ed之前完成。可能性很小
因此,如果不再需要上下文,我更愿意对每个wait
使用configurewait(false)
。谢谢!你的最后一句话是我考虑的方式(我已经,但现在我要坚持更多)。这不会产生明显的区别,但使用ConfigureAwait(false)
也可以避免不必要的Synchroni