2

我知道使用eventwaithandles取消背景工作的常用方法... 但我想知道的是使用while循環來捕獲和暫停背景工作的工作的權利?我這樣編碼:使用while循環取消背景工作

Bool stop = false; 

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
     progressBar1.Minimum = 0; 
     progressBar1.Maximum = 100000; 
     progressBar1.Value = 0; 

     for (int i = 0; i < 100000; i++) 
     { 
      progressBar1.Value++; 

      if (i == 50000) 
       stop = true; 

      while (stop) 
      { } 
     } 
    } 
    private void button1_Click(object sender, EventArgs e) 
    { 
     stop = !stop; 
    } 
+0

沒有它不正確的方式。 –

回答

1

你試過嗎?發生了什麼?這是你想要發生的事情嗎?你有沒有注意到你的電腦風扇正在加速,在緊張的「無所事事」循環中處理來自CPU的所有熱量?

事實是,你不應該首先「暫停」後臺任務;如果你不保持運行,請打斷它。如果您希望能夠稍後恢復,請提供允許的機制。即使讓你的線程有效地等待一個WaitHandle對象也是不對的,因爲它浪費了一個線程池線程。

你在這裏發佈的代碼是實現「暫停」的最糟糕方式。而不是等待一些同步對象,如WaitHandle,您可以使當前線程循環而不中斷,並不斷檢查標誌的值。即使忽略了你是否使用volatile(該代碼示例沒有顯示,但它也不會編譯,所以......),它是可怕的迫使CPU核心做了這麼多的工作,但無處可去。

首先不要暫停您的BackgroundWorker.DoWork處理程序。真。只是不要那樣做。但是,如果你堅持,那麼至少要使用某種可等待的對象,而不是像你在這裏發佈的例子那樣使用「旋轉等待」循環。


下面是一個例子,說明如果你想避免在「暫停」時完全綁定一個線程,你的代碼如何工作。首先,請勿使用BackgroundWorker,因爲它沒有優雅的方式來執行此操作。其次,請使用await&hellip;它具體執行您想要的操作:它允許當前方法返回,但不會丟失其進度。當它等待的東西指示完成時,該方法將繼續執行。

在下面的例子中,我試着猜測叫做RunWorkerAsync()的代碼是什麼樣的。或者說,我只是假設你有一個button2,點擊時你調用該方法來啓動你的工作任務。如果這還不足以讓您指出正確的方向,請通過包含a good, minimal, complete code example來顯示您實際正在做的事情,從而改進您的問題。

// These fields will work together to provide a way for the thread to interrupt 
// itself temporarily without actually using a thread at all. 
private TaskCompletionSource<object> _pause; 
private readonly object _pauseLock = new object(); 

private void button2_Click(object sender, DoWorkEventArgs e) 
{ 
    // Initialize ProgressBar. Note: in your version of the code, this was 
    // done in the DoWork event handler, but that handler isn't executed in 
    // the UI thread, and so accessing a UI object like progressBar1 is not 
    // a good idea. If you got away with it, you were lucky. 
    progressBar1.Minimum = 0; 
    progressBar1.Maximum = 100000; 
    progressBar1.Value = 0; 

    // This object will perform the duty of the BackgroundWorker's 
    // ProgressChanged event and ReportProgress() method. 
    Progress<int> progress = new Progress<int>(i => progressBar1.Value++); 

    // We do want the code to run in the background. Use Task.Run() to accomplish that 
    Task.Run(async() => 
    { 
     for (int i = 0; i < 100000; i++) 
     { 
      progress.Report(i); 

      Task task = null; 

      // Locking ensures that the two threads which may be interacting 
      // with the _pause object do not interfere with each other. 
      lock (_pauseLock) 
      { 
       if (i == 50000) 
       { 
        // We want to pause. But it's possible we lost the race with 
        // the user, who also just pressed the pause button. So 
        // only allocate a new TCS if there isn't already one 
        if (_pause == null) 
        { 
         _pause = new TaskCompletionSource<object>(); 
        } 
       } 

       // If by the time we get here, there's a TCS to wait on, then 
       // set our local variable for the Task to wait on. In this way 
       // we resolve any other race that might occur between the time 
       // we checked the _pause object and then later tried to wait on it 
       if (_pause != null) 
       { 
        task = _pause.Task; 
       } 
      } 

      if (task != null) 
      { 
       // This is the most important part: using "await" tells the method to 
       // return, but in a way that will allow execution to resume later. 
       // That is, when the TCS's Task transitions to the completed state, 
       // this method will resume executing, using any available thread 
       // in the thread pool. 
       await task; 

       // Once we resume execution here, reset the TCS, to allow the pause 
       // to go back to pausing again. 
       lock (_pauseLock) 
       { 
        _pause.Dispose(); 
        _pause = null; 
       } 
      } 
     } 
    }); 
} 

private void button1_Click(object sender, EventArgs e) 
{ 
    lock (_pauseLock) 
    { 
     // A bit more complicated than toggling a flag, granted. But it achieves 
     // the desirable goal. 
     if (_pause == null) 
     { 
      // Creates the object to wait on. The worker thread will look for 
      // this and wait if it exists. 
      _pause = new TaskCompletionSource<object>(); 
     } 
     else if (!_pause.Task.IsCompleted) 
     { 
      // Giving the TCS a result causes its corresponding Task to transition 
      // to the completed state, releasing any code that might be waiting 
      // on it. 
      _pause.SetResult(null); 
     } 
    } 
} 

請注意,以上就像您的原始示例一樣。如果你真的只是一個簡單的單循環變量,從0到100,000重複,並且在一半的時間內停下來,沒有什麼比上面所要求的那麼複雜。您只需將循環變量存儲在某個數據結構中,退出正在運行的任務線程,然後在想要恢復時,傳入當前循環變量值,以便方法可以從正確的索引繼續。

但我假設你的真實世界的例子並不那麼簡單。並且上述策略將適用於任何有狀態處理,編譯器會爲您完成所有重要的存儲中間狀態。

+0

你是對的,我的代碼做我想要的,但最主要的問題是CPU。你的建議是非常有幫助的 – Hnz