如何等待来自不同线程的异步 UI 方法?

How to await an async UI method from a different thread?

我如何优雅地 告诉我的应用程序它应该等待某些异步 (Ask()) 方法的结果,而不是在其当前 (Game) 线程而是在不同的 (UI) 线程上?

我有一个带有两个线程的 Forms 应用程序

用户界面由两种形式组成:

当用户单击 Create Cube 按钮时,UI Thread 将处理此单击事件并(同步)排队一个新的 ()=>Game.Create<Cube>() 操作以供 Game Thread.

Game Thread 将在处理另一帧时获取此操作,并检查用户是否要创建 CubeSphere。如果用户请求 Cube,它应该询问使用第二种形式的用户关于立方体角的所需形状。

问题是,在等待用户决定时,UIGame 线程都不应该被阻塞。因此,Task Game.Create<T>(...) 方法和 Task<CornerChoice> ChoiceForm.Ask() 方法被声明为异步的。 Game Thread 将等待 Create<T>() 方法的结果,而 Create<T>() 方法又应该等待 UI 线程上的 Ask() 方法的结果(因为 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;
        ...
    }
}

在阅读了 Stephen Cleary 链接的一个可能相关的问题 here and Stephen Dougs blog post Task.Run vs Task.Factory.StartNew 并与 Mrinal Kamboj 讨论了这个问题后,我得出的结论是 Task.Run 方法是 [=13] 的包装器=] 对于常见情况。因此,对于我不太常见的情况,我决定将引起痛苦的东西扫到扩展方法中,使调用看起来如下所示:

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