![](/img/trans.png)
[英]When will an async await method return from the UI-Thread? At the first await or at the inner await?
[英]How to await an async UI method from a different thread?
我怎樣才能優雅地告訴我的應用程序應該等待某些異步( Ask()
)方法的結果不是在其當前( Game
)線程上而是在另一個( UI
)線程上呢?
我有一個帶有兩個線程的Forms應用程序
UI Thread
,它運行用戶界面 Game Thread
,它以一種無限循環運行,等待輸入動作並以或多或少恆定的幀速率渲染游戲視圖。 用戶界面包含兩種形式:
MainForm
,帶有Create Cube
按鈕, Create Sphere
按鈕和渲染視圖 ChoiceForm
,要求用戶使用兩個按鈕在Sharp Corners
和Rounded Corners
之間進行選擇。 當用戶單擊“ Create Cube
按鈕時, UI Thread
將處理此單擊事件並(同步)對Game Thread
處理的new ()=>Game.Create<Cube>()
動作進行排隊。
Game Thread
將在處理另一幀時檢索此操作,並檢查用戶是否要創建Cube
或Sphere
。 如果用戶請求Cube
它應該詢問用戶使用第二種形式關於立方角的所需形狀。
問題是,在等待用戶決策時, UI
和Game
線程都不應被阻止。 因此, Task Game.Create<T>(...)
方法和Task<CornerChoice> ChoiceForm.Ask()
方法被聲明為異步。 Game Thread
將等待Create<T>()
方法的結果,而該方法又應等待UI線程上的Ask()
方法的結果(因為ChoiceForm
是在該方法內部創建並顯示的)。
如果這一切都發生在單個UI Thread
生活將相對容易, Create
方法將如下所示:
public class Game
{
private async Task Create<IShape>()
{
CornerChoice choice = await ChoiceForm.Ask();
...
}
}
經過一些試驗和錯誤后,我提出了以下(實際工作)解決方案,但每次我仔細觀察它時,它似乎都會傷到我內部(特別是Create
方法中的Task<Task<CornerChoice>>
部分):
public enum CornerChoice {...}
public class ChoiceForm
{
public static Task<CornerChoice> Ask()
{
...
}
}
public class MainForm
{
private readonly Game _game;
public MainForm()
{
_game = new Game(TaskScheduler.FromCurrentSynchronizationContext());
}
...
}
public class Game
{
private readonly TaskScheduler _uiScheduler;
public Game(TaskScheduler uiScheduler)
{
_uiScheduler = uiScheduler;
}
private async Task Create<IShape>()
{
...
Task<CornerChoice> task = await Task<Task<CornerChoice>>.Factory.StartNew(
async () => await ChoiceForm.Ask(),
CancellationToken.None, TaskCreationOptions.None, _uiScheduler);
CornerChoice choice = await task;
...
}
}
閱讀可能與問題后這里和斯蒂芬另一部阿德日記的博客文章Task.Run VS Task.Factory.StartNew由Stephen Cleary的結合,並與Mrinal Kamboj討論這個問題,我得出的結論是, Task.Run
方法是那種包裝的周圍TaskFactory.StartNew
用於常見情況。 因此,對於我不那么常見的情況,我決定將引起痛苦的事情轉移到擴展方法中,以使調用看起來如下所示:
private async Task Create<IShape>()
{
...
CornerChoice choice = await _uiScheduler.Run(ChoiceForm.Ask);
...
}
使用相應的擴展方法:
public static class ExtensionsForTaskScheduler
{
public static async Task<T> Run<T>(this TaskScheduler scheduler,
Func<Task<T>> scheduledTask)
{
return await await Task<Task<T>>.Factory.StartNew(scheduledTask,
CancellationToken.None, TaskCreationOptions.None, scheduler);
}
}
似乎沒有必要將() => ChoiceForm.Ask()
lambda聲明為async
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.