2010-02-02 91 views
9
for (do it a bunch of times) 
{   
    while (backgroundWorker1.IsBusy && backgroundWorker2.IsBusy && 
      backgroundWorker3.IsBusy && backgroundWorker4.IsBusy && 
      backgroundWorker5.IsBusy) 
    { 
     System.Threading.Thread.Sleep(0001); 
    } 

    if (!backgroundWorker1.IsBusy) 
    { 
     backgroundWorker1.RunWorkerAsync(); 
    } 
    else if (!backgroundWorker2.IsBusy) 
    { 
     backgroundWorker2.RunWorkerAsync(); 
    } 
    else if (!backgroundWorker3.IsBusy) 
    { 
     backgroundWorker3.RunWorkerAsync(); 
    } 
    else if (!backgroundWorker4.IsBusy) 
    { 
     backgroundWorker4.RunWorkerAsync(); 
    } 
    else if (!backgroundWorker5.IsBusy) 
    { 
     backgroundWorker5.RunWorkerAsync(); 
    } 
} 

它運行五次(每個BG工人一次)並陷入困境。背景工作者不要停止忙碌嗎?我如何檢查可用性?背景工作者永遠不會停止忙碌

注意:有5個工作線程,這可以確保他們中的任何一個都不會停下來,始終將工作分配給他們。但他們拒絕告訴我,當他們可用,我認爲將有一個簡單的解決辦法..

- [編輯請求] ---

其實這只是一個虛擬的參數,我刪除它,忘了把它弄出來,我只用它來調用DoWork的,誰做的骯髒的工作:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    timeconsumingfunction(publicstring); 
} 

而且timeconsumingfunction DOES結束。在調試器和每行運行線中進入它,直到結束併到達最後的'}'。這意味着它結束了,對吧?

--- [編輯答案】---- 它的工作與

Application.DoEvents(); 

JUST更換線

System.Threading.Thread.Sleep(0001); 

我想它會運行背景,但沒有收到回答並不更新IsBusy標籤。

謝謝大家,很好的答案,幫了很大忙!

+0

確定作業是否完成的最簡單方法是在RunWorkerCompleted事件上放下斷點並讓程序運行。如果你點擊了斷點,那麼後臺工作者已經完成了這項工作。 – 53an 2010-02-02 12:07:21

回答

34

您的循環導致死鎖,BGWs無法完成。問題是RunWorkerCompleted事件,它在UI線程上引發。 BGW魔法的這一點需要UI線程閒置,它必須抽取其消息循環。問題在於,UI線程不是空閒的,它不是抽取消息,而是卡在for循環中。因此,事件處理程序不能運行並且IsBusy保持爲真。

您需要以不同的方式做到這一點。利用RunWorkerCompleted事件來運行在for循環之後通常運行的代碼。抵制所有在循環內調用Application.DoEvents()的誘惑。

+0

這幾乎就像一個永恆的,並沒有經過它運行。我正在檢查數組中的網站可用性(使用for的索引),因此無法在RunWorkerCompleted中「繼續」。如果您有任何sugestions,請編輯你的答案..我無言以對:( – Marcelo 2010-02-02 13:01:54

+0

,它並完成。我與調試跟着它,和它到達了「}」在指定點時出現了有些睡眠(0001)的運行時間不如運行時間,因爲步進之間有時間,所以我睡了(0001)約20次...然後再次運行,直到'}',但是甚至在doWork的最後一個「}」,BGW不會將isBusy修改爲false – Marcelo 2010-02-02 13:22:22

+3

不會,直到RunWorkerCompleted事件運行完成纔會完成,在DoWork事件處理程序完成後會發生什麼,RWC處理程序是問題,它不能運行,因爲你的UI線程停留在一個循環中。刪除處理程序將是一個快速修復。 – 2010-02-02 13:41:22

0

.IsBusy僅指示背景工作者實際上正在執行操作。它會很忙,直到「某事」完成。看起來「某些事情」沒有完成,讓你的背景工作者忙碌起來。

因此,如果你能解釋什麼是「某些東西」,並且可能需要多長時間才能在主線程上執行「某些事情」會有所幫助。

3

我建議您更改您的代碼以處理RunWorkerCompleted事件,以在您的BackgroundWorker完成其工作時收到通知。有一個示例說明如何在the official documentation中使用BackgroundWorker

2

我在使用後臺工作人員時遇到了同樣的問題,得出的結論是,如果您在循環中使用sleep(),那麼它會卡住。您可以使用RunWorkerCompleted事件並設置布爾標誌來指示每個工作人員何時完成。

或者如果你想放棄線程,不管你可以看看使用線程而不是後臺工作者。但是,那麼您在後臺工作人員提供的事件方面失去了易用性。

+0

Urrgh - 是的,不要偷懶 - 睡眠()剛咬了我太多 – 2012-03-28 15:19:59

1

你的主線程需要抽Windows消息(無論調用while循環Application.DoEvents,或通過使用Systems.Windows.Forms.Timer,而不是循環的更好)。

如果你不抽Windows消息,你的後臺工作的「已完成」的通知將不會被處理,因此狀態保持忙碌。

0

的問題是,無論你內worker.RunWorkerAsync()做永遠不會結束。也許有一些無限循環或類似於你的DoWork事件中定義的東西。

這裏是一個工作的例子,即選擇下一個空閒工人:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Threading; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     private static List<MyWorker> _Workers; 

     static void Main(string[] args) 
     { 
      _Workers = new List<MyWorker>(); 

      for (int i = 0; i < 5; i++) 
      { 
       _Workers.Add(CreateDefaultWorker(i)); 
      } 

      StartJobs(20000); 
      Console.ReadKey(); 
     } 

     private static void StartJobs(int runtime) 
     { 
      Random rand = new Random(); 
      DateTime startTime = DateTime.Now; 

      while (DateTime.Now - startTime < TimeSpan.FromMilliseconds(runtime)) 
      { 
       var freeWorker = GetFreeWorker(); 

       if (freeWorker != null) 
       { 
        freeWorker.Worker.RunWorkerAsync(new Action(() => DoSomething(freeWorker.Index, rand.Next(500, 2000)))); 
       } 
       else 
       { 
        Console.WriteLine("No free worker available!"); 
        Console.WriteLine("Waiting for free one..."); 
        WaitForFreeOne(); 
       } 
      } 
     } 

     private static MyWorker GetFreeWorker() 
     { 
      foreach (var worker in _Workers) 
      { 
       if (!worker.Worker.IsBusy) 
        return worker; 
      } 

      return null; 
     } 

     private static void WaitForFreeOne() 
     { 
      while (true) 
      { 
       foreach (var worker in _Workers) 
       { 
        if (!worker.Worker.IsBusy) 
         return; 
       } 
       Thread.Sleep(1); 
      } 
     } 

     private static MyWorker CreateDefaultWorker(int index) 
     { 
      var worker = new MyWorker(index); 

      worker.Worker.DoWork += (sender, e) => ((Action)e.Argument).Invoke(); 
      worker.Worker.RunWorkerCompleted += (sender, e) => Console.WriteLine("Job finished in worker " + worker.Index); 

      return worker; 
     } 

     static void DoSomething(int index, int timeout) 
     { 
      Console.WriteLine("Worker {1} starts to work for {0} ms", timeout, index); 
      Thread.Sleep(timeout); 
     } 
    } 

    public class MyWorker 
    { 
     public int Index { get; private set; } 
     public BackgroundWorker Worker { get; private set; } 

     public MyWorker(int index) 
     { 
      Index = index; 
      Worker = new BackgroundWorker(); 
     } 
    } 
} 
1

我也有類似的問題,我做了什麼來解決它與一個try... catch... and finally...聲明附上的主要功能。

-1

你的問題的一個可行的解決方案在下面的例子中給出。像Hans Passant解釋你的代碼在後臺工作中運行,但每個線程的RunWorkerCompleted只在ui線程中被評估。爲此,您可以將請求排入線程的UI隊列ThreadPool。用戶界面用這種方式評估RunWorkerCompletedEvent,然後跳轉回你的代碼。

for (int i = 0; i < 999999; ++i) 
{ 
    System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback((x) => 
    { 
     while (backgroundWorker1.IsBusy && backgroundWorker2.IsBusy) 
     { 
      System.Threading.Thread.Sleep(0); 
     } 
     // Code that is beging executed after all threads have ended. 
     myCode(); 
    })); 

    if (!backgroundWorker1.IsBusy) 
    { 
     backgroundWorker1.RunWorkerAsync(); 
    } 
    else if (!backgroundWorker2.IsBusy) 
    { 
     backgroundWorker2.RunWorkerAsync(); 
    }   
} 
+0

沒有給出理由的downvoting既不有用也不好。這個答案是對問題的有效答案,並展示瞭如何使用盡可能多的提問者代碼擺脫僵局。 – codingdave 2014-07-28 11:16:56