我有一個使用 Prism 的 WPF 應用程式,并且我發現了使用 Async 方法的額外行為。
我有一個帶有這樣的異步方法的類
public class ConfigurationManager(){
public async Task<IList<T>> LoadConfigurationAsync<T>(){
Tuple<IList<T>, bool> tuple = await LoadConfigurationItemsAsync();
return tuple.Item1;
}
private async Task<Tuple<IList<T>, bool>> LoadConfigurationItemsAsync<T>(){
await Task.Run(() =>
{
});
return new Tuple<IList<T>, bool>(configList.list, succes);
}
}
而且我需要以同步形式呼叫它們,因為我需要 ViewModelManager 的建構式中的結果,并且我嘗試使用 Result,因為它是以同步方式獲取結果的方法之一。
public class ViewModelManager{
private readonly ConfigurationManager _configManager;
private void LoadConfiguration(){
var config = _configManager.LoadConfigurationAsync().Result;
}
}
令我驚訝的是,這會導致應用程式在 Result 呼叫中被阻塞,我知道 Result 是阻塞的,但并非總是如此,該方法的回傳行永遠不會被執行。我嘗試使用它來呼叫它Task.Run并且它有效
private void LoadConfiguration(){
var config = Task.Run(() => _configManager.LoadConfigurationAsync()).Result;
}
我不知道這里發生了什么以及為什么呼叫結果會阻止應用程式以及為什么使用Task.Run它會起作用。這就像呼叫兩個任務,因為該方法已經回傳一個Task.
uj5u.com熱心網友回復:
訪問.Result總是一個錯誤(在“我已經檢查過它是否已完成”周圍有一個小警告,關于價值任務和單一訪問的次要警告;老實說:“總是”比知道警告更有用的建議! )。
在最好的情況下,您已經實作了“異步同步”并無緣無故地占用了一個執行緒,這會影響可伸縮性并可能會影響執行緒池。
但是,如果有一個同步背景關系,你可以 - 正如你所發現的 - 死鎖的事情。同步背景關系充當作業管理器,在 WPF 的情況下:意味著默認情況下await通過 UI 執行緒匯集回呼和s。所以想象一下:
- 您的 UI 執行緒運行,在后臺啟動某些東西,然后到達
.Result并等待- 尚未回傳主應用程式回圈 - 您的后臺事情完成,并嘗試將掛起的操作指示為完成,即觸發與
await該操作相關聯的延續 - 邏輯說“哦,我看到了
await一個同步背景關系 - 我需要通過它來推動作業”并在主應用程式回圈中添加一些內容- 添加到應用程式回圈的東西是告訴任務它完成的東西,以便
.Result可以完成
- 添加到應用程式回圈的東西是告訴任務它完成的東西,以便
- 繁榮:僵局
正是因為 UI 執行緒卡在 上.Result,所以應用程式回圈永遠不會真正將 標記.Result為已完成。有一些改進的方法.ConfigureAwait(false),但主要目的是避免在你希望它們在后臺運行時在 UI 執行緒上運行它們;不應該使用這種方法來防止死鎖,即使這恰好是一個巧合的副作用。
這里的“修復”很簡單:不要使用.Result(或.Wait())。你需要await它,并采取相應的行動。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/481275.html
上一篇:如何更好地為另一個班級創建財產
