2012-04-23 19 views
4

我有一個後臺工作人員,通過ReportProgress定期更新GUI。在檢查取消時發送背景人員進入睡眠狀態

更新以定期間隔進行,例如每隔5秒鐘進行一次,或者可能是20秒。爲了在設定的時間執行更新,我會將工作進程發送到睡眠狀態持續一段時間,並在喚醒時使用新信息更新GUI。

工作人員支持取消,並且在睡眠之外它正確取消。

我希望能夠在等待期間調用取消,但是將線程發送到睡眠狀態使得這是不可能的。

我假設我將不得不調用一個循環,並檢查作爲循環模擬線程睡眠的一部分的取消。

什麼是實現這一目標的最佳方法,我的時機完全與我的嘗試。

  long counter = 0; 
      long sleepfor = timelinespeed*1000; 
      int timelinespeed = 10; 

       while (counter != sleepfor) 
       { 

        Thread.Sleep(1); 

        counter++; 

        if (bkgwk.CancellationPending) 
        { 
         cancelled = true; 
         e.Cancel = true; 
         bkgwk.Dispose(); 
         break; 
        } 

       } 

回答

13

你走錯了路。

  • Bgw正在使用ThreadPool,因此應該用於(相對)較短的動作,通常在0.5秒以內。不建議使用無限期保存。
  • 你幾乎從不在任何線程上使用Sleep()。當然不會超過幾個毫秒。
  • 使用Sleep(1)在忙等待5秒鐘是CPU的一大浪費。你正在傷害你的其他線程。至少考慮加大到Sleep(100)左右。找到您允許取消的最大延遲時間。

作爲解決方案:使用計時器。你有一個定期的任務,使用正確的工具。

使用System.Threading.Timer爲基於ThreadPool的後臺工作。通過Dispatcher.Invoke()更新GUI。

使用Dispatcher.Timer在主線程上執行小部分工作(小於0.1秒)。您可以直接更新GUI。

+0

+1計時器建議。如果你使用wpf,你也可以看看Dispatcher計時器。取消操作變得簡單 - 您只需停止計時器(假設允許任何當前任務完成即可) – JMarsch 2012-04-23 20:34:58

+0

System.Threading.Timer是否允許我使用信息更新GUI? MSDN建議一個System.Windows.Forms.Timer,但是這是單線程? – Damo 2012-04-23 20:36:57

+0

Forms.Timer和Dispatcher.Timer都在主線程上運行,所以我不會將它們用於繁重的操作。更新每個其他線程的GUI是相同的:使用Dispatcher.Invoke() – 2012-04-23 20:39:22

2

您可以使用ManualResetEvent來允許您的線程等待而不用輪詢,並允許另一個線程在任何時候將其喚醒。

ManualResetEvent以及任何其他線程同步對象有一個WaitOne方法可以等待事件被設置或超時。所以通常你可以讓WaitOne等待你想要等待的數量(這將會像Thread.Sleep一樣工作),但是主線程可以在任何時候喚醒後臺線程。

ManualResetEvent backgroundWakeEvent = new ManualResetEvent(false); 
.... 
bkgwk.CancelAync(); 
backgroundWaitEvent.Set(); 
.... 
backgroundWakeEvent.WaitOne(5000); 

if (bkgwk.CancellationPending) 
{ 
    cancelled = true; 
    e.Cancel = true; 
    bkgwk.Dispose(); 
    break; 
} 
2

甚至更​​好,使用the Reactive Extensions framework並有「工人」觀察間隔事件流。然後流可以很容易地處理停止產生新的事件,從而終止工人。帶有消除結合這一次性的,你有一個單一的一次性的,以同時停止間隔定時器和任何工人在做其工作:

var cancel = new BooleanDisposable(); 
var subscription = Observable.Interval(TimeSpan.FromSeconds(20)) 
          .ObserveOn(Scheduler.DispatcherScheduler) 
          .Subscribe(i => PerformWorkOnUI(cancel)); 
var uiWork = new CompositeDisposable(cancel, subscription); 

要取消計時器事件流,你只需要處置的複合取消/訂閱:

uiWork.Dispose(); 

澄清:ObserveOn(Scheduler.DispatcherScheduler)確保訂閱將調度線程上調用。

相關問題