2017-07-24 115 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(); 
}