如何等待来自不同线程的异步UI方法
本文关键字:线程 异步 方法 UI 何等待 等待 | 更新日期: 2023-09-27 18:00:23
我如何才能优雅地告诉我的应用程序,它应该等待某个异步(Ask()
)方法的结果,而不是在其当前(Game
)线程上,而是在另一个(UI
)线程上
我有一个带有两个线程的Forms应用程序
- 运行用户界面的强制性
UI Thread
- 和第二个
Game Thread
,它以某种无限循环运行,等待输入动作并以或多或少恒定的帧速率呈现游戏视图
用户界面由两种形式组成:
- 带有
Create Cube
按钮、Create Sphere
按钮和渲染视图的简单MainForm
- 以及要求用户使用两个相应按钮在CCD_ 10和CCD_
当用户点击Create Cube
按钮时,UI Thread
将处理此点击事件,并(同步)排队等待Game Thread
处理新的()=>Game.Create<Cube>()
动作。
Game Thread
将在处理另一帧时获取该动作,并检查用户是否想要创建Cube
或Sphere
。如果用户请求Cube
,则应该使用第二种形式询问用户关于立方体角的期望形状。
问题是,在等待用户决定时,UI
和Game
线程都不应该被阻塞。因此,Task Game.Create<T>(...)
方法和Task<CornerChoice> ChoiceForm.Ask()
方法被声明为异步。Game Thread
将等待Create<T>()
方法的结果,而CCD_25又应在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;
...
}
}
在阅读了这里的一个可能相关的问题和Stephen Dougs的博客文章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
。