我如何优雅地告诉我的应用程序它应该等待某个异步(Ask()
)方法的结果,而不是在其当前(Game
)线程上,而是在不同的( UI
) 线程?
我有一个带有两个线程的 Forms 应用程序
- 强制性的
UI Thread
, 运行用户界面 - 和第二个
Game Thread
,它在某种无限循环中运行,等待输入操作并以或多或少恒定的帧速率渲染游戏 View 。
用户界面由两种形式组成:
- 一个简单的
MainForm
用Create Cube
按钮,一个Create Sphere
按钮和渲染 View - 和自定义
ChoiceForm
要求用户在Sharp Corners
之间进行选择和Rounded Corners
使用两个相应的按钮。
当用户点击 Create Cube
按钮,UI Thread
将处理此点击事件并(同步)排队一个新的 ()=>Game.Create<Cube>()
Game Thread
处理的 Action .
Game Thread
将在处理另一帧时获取此操作并检查用户是否想要创建 Cube
或 Sphere
.如果用户请求 Cube
它应该询问使用第二种形式的用户关于立方体角的所需形状。
问题是,UI
都不是也不是 Game
线程在等待用户决定时应该被阻塞。因此 Task Game.Create<T>(...)
方法和 Task<CornerChoice> ChoiceForm.Ask()
方法被声明为异步。 Game Thread
将等待 Create<T>()
的结果方法,它又应该等待 Ask()
的结果UI 线程上的方法(因为 ChoiceForm
是在该方法内部创建和显示的)。
如果这一切都发生在一个 UI Thread
上生活会相对轻松,Create
方法看起来像这样:
public class Game
{
private async Task Create<IShape>()
{
CornerChoice choice = await ChoiceForm.Ask();
...
}
}
经过反复试验,我提出了以下(实际有效的)解决方案,但每次我仔细观察它时,它似乎都会伤害到我内心的某个地方(尤其是 Task<Task<CornerChoice>>
方法中的 Create
部分):
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;
...
}
}
最佳答案
阅读可能相关的问题后here和 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
。
关于c# - 如何等待来自不同线程的异步 UI 方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31115887/