2017-10-10 68 views
1

我們有一個WinForm應用程序,它具有所有其他窗體繼承的基本窗體。
在基礎窗體上有一個Delete按鈕,Delete按鈕調用一個名爲DeleteData的虛擬bool方法。如何等待任務完成而WinForm中沒有死鎖,其中開發人員無法向當前方法添加異步

public virtual void DeleteButton_Click(object sender, EventArgs e) 
{ 
    if (DeleteData()) 
    { 
     // run some cleanup code 
     DoSomeCleanup(); 
    } 
} 

public virtual bool DeleteData() 
{ 
    throw new NotImplementedException("Not implemented."); 
} 

子窗體已經覆蓋了DeleteData方法

public override bool DeleteData() 
{ 
    try 
    { 
     // Delete some data 

     // Call async method that does some UI stuff and wait on it to finish 
     SomeSharedAsyncMethod(); 

     return true; 
    } 
    catch(Exception ex) 
    { 
     // Handle exception 

     return false; 
    } 
} 

這裏是收集,SomeSharedAsyncMethod是標記爲異步和它裏面,它做了一些UI類的東西結合到文本框,並調用此方法別人因此該方法必須保持標記爲「異步」

public async Task SomeSharedAsyncMethod() 
{ 
    // Some code that has await and also some code that updates textboxes or other UI controls. 

    await Task.Delay(2000); 
    SomeTextBox.Text = "Some Text"; 
} 

因爲基本形式看DeleteData我,我不能「異步」添加到「DeleteData」方法運行「DoSomeCleanup」和「DoSomeCleanup」將在DeleteData完成前調用。
我們還假設我無法將「異步」添加到刪除按鈕,因爲我沒有該項目的控制權。
我也不想重寫DeleteButton_Click,因爲我不想複製位於基本表單DeleteButton_Click內的所有代碼。

這裏有一些事情我已經嘗試:

public override bool DeleteData() 
{ 
    // Delete some data 

    // Call async method that does some UI stuff and wait on it to finish 

    // Causes a deadlock 
    SomeSharedAsyncMethod().Wait(); 

    // RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method. 
    SomeSharedAsyncMethod().RunSynchronously(); 

    // This will not wait on SomeSharedAsyncMethod to execute 
    SomeSharedAsyncMethod().ConfigureAwait(false); 

    // Cross-thread operation not valid 
    Task.Run(async() => { await SomeSharedAsyncMethod(); }).Wait(); 

    // This will not wait on SomeSharedAsyncMethod to execute 
    Task.Run(() => { SomeSharedAsyncMethod(); }).Wait(); 

    // This will not wait on SomeSharedAsyncMethod to execute 
    Task.Run(async() => { await SomeSharedAsyncMethod().ConfigureAwait(false); }).Wait(); 

    // This will not wait on SomeSharedAsyncMethod to execute 
    Task.Factory.StartNew(() => 
    { 
     SomeSharedAsyncMethod().ConfigureAwait(false); 
    }, Task.Factory.CancellationToken, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()).Wait(); 

    return true; 
} 

我們正在尋找獲得DeleteData方法來運行其所有的代碼,而不是返回,直到所有代碼行已完成的一種方式,這包括SomeSharedAsyncMethod 。

+0

看着鎖:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement – Sorceri

+1

檢查了這一點:https://stackoverflow.com/a/5097066/5779825。希望它有幫助 – oopsdazie

回答

0

我不認爲有一種方法可以在DeleteData內等待SomeSharedAsyncMethod

DeleteData方法佔用UI線程。它需要SomeSharedAsyncMethod才能完成以釋放UI線程,但SomeSharedAsyncMethodDeleteData之後開始並且它也需要UI線程。換句話說,你需要在UI線程上運行一些東西才能釋放它。這是典型的死鎖例子。而這是非常重要的,爲什麼你不應該混合async.Wait()擺在首位。

那麼,這可以如何解決?我建議從SomeSharedMethodAsync中刪除所有與UI相關的代碼,並將.ConfigureAwait(false)添加到其中的所有異步方法調用中。

public override bool DeleteData() 
{ 
    SomeSharedMethodAsync().ConfigureAwait(false).Wait(); 
    UpdateUI(); 
} 

async Task SomeSharedAsyncMethod() 
{ 
    // do all async stuff here, but DON'T update the UI 
    await Task.Delay(2000).ConfigureAwait(false); 
} 

void UpdateUI() 
{ 
    // update the UI here 
    SomeTextBox.Text = "Some Text"; 
} 

當然,這應該只作爲最後的手段。使所有以DeleteButton_Click異步開始的方法更受歡迎。

+0

「oopsdazie - above」發送的文章工作並不需要修改其他方法,但我試圖讓Nito.AsyncEx執行相同的操作。 https://stackoverflow.com/a/5097066/5779825 – goroth

0

有兩個選項我測試,並且都工作。

  1. How would I run an async Task<T> method synchronously?

這是把我的oopsdazie在評論上方。

請查看:stackoverflow.com/a/5097066/5779825。希望它能幫助 - oopsdazie 10月10日在20:35

這是一個從這裏重新發布 https://social.msdn.microsoft.com/Forums/en-US/163ef755-ff7b-4ea5-b226-bbe8ef5f4796/is-there-a-pattern-for-calling-an-async-method-synchronously?forum=async

它基本上是一個異步的輔助類,針對其自身的SynchronizationContext工作。

  • 使用Stehpen克利 - NITO AsyncEx的NuGet
  • https://github.com/StephenCleary/AsyncEx

    Nito.AsyncEx.AsyncContext.Run(async() => await SomeSharedAsyncMethod()); 
    

    我去與選項2,因爲它有很多其他方便的異步助手