簡體   English   中英

如何從不同的線程等待異步UI方法?

[英]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 CornersRounded Corners之間進行選擇。

當用戶單擊“ Create Cube按鈕時, UI Thread將處理此單擊事件並(同步)對Game Thread處理的new ()=>Game.Create<Cube>()動作進行排隊。

Game Thread將在處理另一幀時檢索此操作,並檢查用戶是否要創建CubeSphere 如果用戶請求Cube它應該詢問用戶使用第二種形式關於立方角的所需形狀。

問題是,在等待用戶決策時, UIGame線程都不應被阻止。 因此, 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM