2017-07-24 72 views
1

在下面的示例中,我在Sync方法(UI)中调用Async方法。 在异步方法中,我调用另一个异步方法(例如API调用),但我也调用其他同步方法(例如更新组合框)。现在我使用Invoke((MethodInvoker ...)来调用每个同步方法,这是否是正确的方法,它可以更好吗?不,我还必须考虑使用Invoke((MethodInvoker ...)在同步方法中调用同步方法。在一种方法中调用同步和异步方法(api/UI)的正确方法是什么

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

难道'任务task = SomeMethodAsync()'你非异步方法更有意义。MethodInvoker代码的逻辑是什么? – Magnus

+0

为什么它会更有意义? Invoke调用的方法是属于UI线程的同步方法。 SomeOtherMethodAsync是api调用(异步)。 –

+0

这条线对我来说毫无意义。您创建一个任务只是为了等待另一个任务。为什么不直接返回第一个任务(SomeMethodAsync的结果)。 – Magnus

回答

3

让我们打破这里发生了什么

当您control_SelectionValueChanged处理程序便会启动,我认为我们在UI线程上运行,则然后:。

  • 揭开序幕SomeMethodAsync上线池线程通过Task.Run。这不会阻止UI线程。
  • 一旦线程池线程开始执行SomeMethodAsync您正在要求运行时通过调用Control.Invoke将您的返回回传给UI线程。虽然SomeMethodA正在UI线程上执行,但同时也会阻塞线程池线程。
  • 然后,您解除对线程池线程的阻塞并要求它执行一些其他的async方法。整个操作将保持关闭UI线程(除非里面有SomeOtherMethodAsync东西时髦,即另一个Control.Invoke呼叫)
  • await返回到一个线程池线程 - 这可能是同一个线程池中的线程作为前await,或不同的 - 这是由TaskScheduler
  • 如果variabletrue,则在UI线程上执行SomeMethodB(同时再次阻塞线程池线程)。
  • 最后,您在UI线程上执行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(); 
} 

以上是在行为方面相似(虽然不是完全等同),不过,是不是更容易阅读?

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(); 
}