簡體   English   中英

如何保證Task在當前線程上同步運行?

[英]How to guarantee that a Task runs synchronously on the current thread?

我知道在這個網站和其他網站上有幾個類似的問題,但是由於某種原因,這種做法的標准方式似乎不適用於我的情況。 完成此要求的常規方法是使用TaskScheduler.FromCurrentSynchronizationContext()作為相關Task.Factory.StartNew重載中TaskScheduler輸入參數:

// Set uiTaskScheduler whilst on the UI thread
TaskScheduler uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
...
Task.Factory.StartNew(() => SomeMethodToRunAsynchronously(), 
    CancellationToken.None, TaskCreationOptions.None, uiTaskScheduler);

看來這足以安排一個Task在UI線程上運行,但它似乎在我的情況下不起作用。 在我的例子中,我有一個UiThreadManager類,其中包含RunAsynchronously方法:

public Task RunAsynchronously(Action method)
{
    return Task.Run(method);
}

這部分工作正常。 我面臨的問題是,在運行單元測試時,這個類被替換為MockUiThreadManager類(都實現了應用程序代碼使用的IUiThreadManager接口),我似乎無法強制方法在UI線程上運行:

public Task RunAsynchronously(Action method)
{
    return Task.Factory.StartNew(() => method(), 
        CancellationToken.None, TaskCreationOptions.None, UiTaskScheduler);
}

MockUiThreadManager類有一個static UiTaskScheduler屬性,它在UI線程上設置(如下所示),所以我假設所有通過上述方法的代碼都會在該線程上按預期運行:

SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
MockUiThreadManager.UiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();

但是,在運行單元測試時,我注意到它在測試的代碼之前完成。 因此,我在每個斷點的Visual Studio立即窗口中添加了一些斷點並調用了System.Threading.Thread.CurrentThread.ManagedThreadId ,當然,當代碼通過上述方法傳遞時,線程ID也發生了變化。

基本上,我正在尋找一種方法來偽造基於Task的異步調用,該異步調用在單元測試期間運行的RunAsynchronously方法將確保該方法實際上在UI線程上同步運行。 有人看到我做錯了什么,還是有其他建議?

更新>>>

好的,我在這里使用了關於UI線程的錯誤術語。 單元測試不在UI線程上運行,因為沒有UI ...但是,情況保持不變。 為了澄清,我只需要我的測試來同步運行應用程序代碼並在它啟動的主要單個線程上運行。 問題是當應用程序運行時,有很多基於Task的異步代碼,需要同步通過MockUiThreadManager類進行測試。

繼續我的搜索之后,我現在找到了我正在尋找的解決方案,只需幾行代碼就可以實現,所以絕對不需要實現我自己的SynchronizationContext類。 看看它有多簡單,我很驚訝我之前沒有找到它。 在運行單元測試時使用的MockUiThreadManager類中,我現在有了以下代碼:

public Task RunAsynchronously(Action method)
{
    Task task = new Task(method);
    task.RunSynchronously();
    return task;
}

我可以確認它完成它所說的並在運行測試的同一線程上同步運行method函數。

為了完整起見, Task.RunSynchronously方法還有一個覆蓋,它接受一個TaskScheduler ,但在我的情況下這是不必要的,所以我不再需要我的MockUiThreadManager.UiTaskScheduler屬性了。

new SynchronizationContext()返回一個新的默認SynchronizationObject,用於調度線程池( 引用源 )上的工作。

TaskScheduler.FromCurrentSynchronizationContext()返回使用任務調度SynchronizationContext.Current您剛才設置使用線程池同步方面SynchronizationContext.SetSynchronizationContext

這意味着在該調度程序上的調度任務將使用線程池來執行它們,而不是特定的線程。

通常,甚至不可能在特定線程上安排工作, 除非該線程具有某種類型的消息隊列。 這就是為什么你可以安排工作在UI線程上運行的原因。

我不知道你使用哪個單元測試框架。 它可能有一個UI來顯示測試結果,但這並不意味着測試是在該線程上運行的。


除了編寫自己的SynchronizationContext類之外,我不知道如何解決這個問題。 你想要對必須在UI線程上運行的東西進行單元測試也感覺有點奇怪,因為在我看來,UI本身是不適合單元測試的東西。 如果您使用的是ViewModelController類的東西,那么當然您可以對它們進行單元測試,但無論您使用何種同步上下文,它們都應該可以正常運行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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