AJ *_*son 8 .net c# winforms async-await
我有一个WinForms应用程序,我有一些代码需要在UI线程上运行.但是,在await不同的线程上运行后的代码.
protected override async void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
// This runs on the UI thread.
mainContainer.Controls.Clear();
var result = await DoSomethingAsync();
// This also needs to run on the UI thread, but it does not.
// Instead it throws an exception:
// "Cross-thread operation not valid: Control 'mainContainer' accessed from a thread other than the thread it was created on"
mainContainer.Controls.Add(new Control());
}
Run Code Online (Sandbox Code Playgroud)
我也试过明确添加ConfigureAwait(true),但没有区别.我的理解是,如果我省略ConfigureAwait(false),那么继续应该在原始线程上运行.在某些情况下这是不正确的吗?
我还注意到,如果我在await之前向集合添加一个控件,那么延续会在正确的线程上神奇地运行.
protected override async void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
// This runs on the UI thread.
mainContainer.Controls.Add(new Control());
mainContainer.Controls.Clear();
var result = await DoSomethingAsync();
// This also runs on the UI thread now. Why?
mainContainer.Controls.Add(new Control());
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:
供参考,以下是重要部分DoSomethingAsync.它使用RestSharp提交HTTP请求.
protected async Task DoSomethingAsync()
{
IRestRequest request = CreateRestRequest();
// Here I await the response from RestSharp.
// Client is an IRestClient instance.
// I have tried removing the ConfigureAwait(false) part, but it makes no difference.
var response = await Client.ExecuteTaskAsync(request).ConfigureAwait(false);
if (response.ResponseStatus == ResponseStatus.Error)
throw new Exception(response.ErrorMessage ?? "The request did not complete successfully.");
if (response.StatusCode >= HttpStatusCode.BadRequest)
throw new Exception("Server responded with an error: " + response.StatusCode);
// I also do some processing of the response here; omitted for brevity.
// There are no more awaits.
}
Run Code Online (Sandbox Code Playgroud)
我的理解是,如果我省略ConfigureAwait(false),那么继续应该在原始线程上运行.在某些情况下这是不正确的吗?
实际发生的是await默认捕获当前上下文,并使用此上下文来恢复该async方法.SynchronizationContext.Current除非它是这样的,否则它就是null这种情况TaskScheduler.Current(通常是线程池上下文).大多数情况下,UI线程有一个UI SynchronizationContext- 在WinForms的情况下,是一个实例WinFormsSynchronizationContext.
我还注意到,如果我在await之前向集合添加一个控件,那么延续会在正确的线程上神奇地运行.
没有线程SynchronizationContext自动启动.SynchronizationContext创建第一个控件时,将按需安装WinForms .这就是您在创建控件后在UI线程上看到它恢复的原因.
由于搬到这OnLoad是一个可行的解决方案,我建议你去做.唯一的其他选项(在创建控件之前在UI线程上恢复)是在第一个之前手动创建控件await.
似乎发生了一些奇怪的事情OnHandleCreated。我的解决方案是使用OnLoad。我对这个解决方案非常满意,因为在我的情况下确实没有理由使用OnHandleCreated。
我仍然很好奇为什么会发生这种情况,所以如果有人知道,请随时发布另一个答案。
编辑:
我发现了真正的问题:事实证明我是Form.ShowDialog()在 a 之后调用的ConfigureAwait(false)。因此,表单是在 UI 线程上构建的,但随后我ShowDialog在非 UI 线程上调用。我很惊讶这竟然有效。
我已经删除了,ConfigureAwait(false)所以现在ShowDialog正在 UI 线程上调用。
| 归档时间: |
|
| 查看次数: |
1255 次 |
| 最近记录: |