在下面的示例中,我在Sync方法(UI)中调用Async方法。 在Async方法中,我调用另一个Async方法(例如api调用),但我也调用其他同步方法(例如更新组合框)。现在我使用Invoke((MethodInvoker ...用于每个同步方法调用。这是正确的方法,它可以更好吗?不,我也必须考虑使用Invoke((MethodInvoker ...在异步调用同步方法时)。
private void control_SelectionValueChanged(Object sender, EventArgs e)
{
Task task = Task.Run(async () => await SomeMethodAsync());
}
private async Task SomeMethodAsync()
{
Invoke((MethodInvoker)(() => SomeMethodA))
bool variable = await SomeOtherMethodAsync()
if ( variable ) Invoke((MethodInvoker)(() => SomeMethodB))
Invoke((MethodInvoker)(() => SomeMethodC))
}
答案 0 :(得分:3)
让我们分解这里发生的事情。
当你的control_SelectionValueChanged
处理程序触发时,我假设我们正在UI线程上运行。然后你:
SomeMethodAsync
在线程池线程上启动Task.Run
。这不会阻止UI线程。SomeMethodAsync
,您就要求运行时通过调用Control.Invoke
将返回封送到UI线程。当SomeMethodA
正在UI线程上执行时,您也同时阻塞了线程池线程。async
方法。整个操作将保持在UI
主题之外(除非SomeOtherMethodAsync
内有一些时髦的东西,即另一个Control.Invoke
来电)await
之后返回一个线程池线程 - 这可能是与await
之前相同的线程池线程,或者是另一个线程池线程 - 这是最高为TaskScheduler
。variable
为true
,则在UI线程上执行SomeMethodB
(同时再次阻止线程池线程)。SomeMethodC
(最后一次阻塞线程池线程)。正如您所看到的,大部分时间SomeMethodAsync
正在执行(等待SomeOtherMethodAsync
所花费的时间除外,以及Control.Invoke
来电之间的短暂时间段)您仍在使用UI线程,但您也阻止您的线程池线程。所以你现在正在占用两个线程,主要是只有一个线程正在做有用的工作 - 另一个只是坐在那里等待。
除了非常可怕的阅读之外,这是非常低效的。
考虑以下重写:
private async void control_SelectionValueChanged(Object sender, EventArgs e)
{
try
{
await SomeMethodAsync();
}
catch (Exception ex)
{
// We're an async void, so don't forget to handle exceptions.
MessageBox.Show(ex.Message);
}
}
private async Task SomeMethodAsync()
{
// We're on the UI thread, and we will stay on the UI
// thread *at least* until we hit the `await` keyword.
SomeMethodA();
// We're still on the UI thread, but if `SomeOtherMethodAsync`
// is a genuinely asynchronous method, we will go asynchronous
// as soon as `SomeOtherMethodAsync` hits the its `await` on a
// `Task` that does not transition to `Completed` state immediately.
bool variable = await SomeOtherMethodAsync();
// If you need stronger guarantees that `SomeOtherMethodAsync`
// will stay off the UI thread, you can wrap it in Task.Run, so
// that its synchronous portions (if any) run on a thread pool
// thread (as opposed to the UI thread).
// bool variable = await Task.Run(() => SomeOtherMethodAsync());
// We're back on the UI thread for the remainder of this method.
if ( variable ) SomeMethodB();
// Still on the UI thread.
SomeMethodC();
}
以上在行为方面类似(尽管不完全相同),但阅读起来不是更容易吗?
答案 1 :(得分:2)
我建议不要混合它们。但是,您在事件处理程序中的事实允许规则中的例外,您可以async void
private async void control_SelectionValueChanged(Object sender, EventArgs e) {
SomeMethodA(); //On UI
bool variable = await SomeOtherMethodAsync(); // Non blocking
//Back on UI
if ( variable ) SomeMethodB();
SomeMethodC();
}