2010-02-02 94 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