在下面的示例中,我在同步方法 (UI) 中调用异步方法。 在异步方法中,我调用了另一个异步方法(例如 api 调用),但我也调用了其他同步方法(例如更新组合框)。现在我对每个同步方法调用都使用 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))
}
最佳答案
让我们分解一下这里发生的事情。
当您的 control_SelectionValueChanged
处理程序触发,我假设我们正在 UI 线程上运行。然后你:
- 开始
SomeMethodAsync
通过Task.Run
在线程池线程上.这不会阻塞 UI 线程。 - 一旦线程池线程开始执行
SomeMethodAsync
您要求运行时通过调用Control.Invoke
将您返回 到 UI 线程.同时SomeMethodA
正在 UI 线程上执行,同时您也在阻塞您的线程池线程。 - 然后您解除线程池线程的阻塞并要求它执行其他一些
async
方法。整个操作将远离UI
线程(除非SomeOtherMethodAsync
里面有一些奇怪的东西,即另一个Control.Invoke
调用) - 在
await
之后您返回到 a 线程池线程 - 这可能与await
之前的线程池线程相同,或不同的 - 这取决于TaskScheduler
. - 如果
variable
是true
,你执行SomeMethodB
在 UI 线程上(再次阻塞线程池线程)。 - 最后,你执行
SomeMethodC
在 UI 线程上(最后一次阻塞线程池线程)。
如您所见,大部分时间 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();
}
以上在行为方面是相似的(虽然不完全等同),但不是更容易阅读吗?
关于c# - 在一种方法中调用同步和异步方法(api/UI)的正确方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45280422/