BackgroundWorkers永远不会停止忙碌

时间:2010-02-02 11:14:11

标签: c# .net visual-studio multithreading backgroundworker

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);
}

时间消耗功能结束了。在调试器中逐步进入它并在每行运行一行,直到结束并到达最后的'}'。这意味着它结束了,对吧?

--- [编辑答案] ---- 它通过JUST替换线

System.Threading.Thread.Sleep(0001);

Application.DoEvents();

我猜它会运行后台,但是没有收到答案,也没有更新IsBusy标签。

谢谢大家,很棒的答案,帮了很多忙!

8 个答案:

答案 0 :(得分:34)

您的循环导致死锁,BGW无法完成。问题是RunWorkerCompleted事件,它是在UI线程上引发的。 BGW魔法的那一点需要UI线程空闲,它必须抽取其消息循环。问题是,UI线程不是空闲的,它不是在发送消息,而是停留在for循环中。因此,事件处理程序无法运行且IsBusy保持为真。

你需要以不同的方式做到这一点。利用RunWorkerCompleted事件运行此for循环后通常运行的代码。抵制在循环中调用Application.DoEvents()的所有诱惑。

答案 1 :(得分:3)

我建议您更改代码以处理RunWorkerCompleted事件,以便在BackgroundWorker完成工作时收到通知。有一个示例说明如何使用the official documentation中的BackgroundWorker

答案 2 :(得分:2)

我在使用后台工作程序时遇到了同样的问题,得出的结论是,如果你在循环中使用sleep(),它就会卡住。您可以使用RunWorkerCompleted事件并设置一个布尔标志来指示每个工作程序何时完成。

或者,如果您想要中止线程,无论您是否可以使用线程而不是后台工作程序。然而,您在背景工作者提供的事件方面失去了易用性。

答案 3 :(得分:1)

你的主线程需要抽取Windows消息(在你的while循环中调用Application.DoEvents,或者更好的方法是使用Systems.Windows.Forms.Timer而不是循环)。

如果您不提取Windows消息,则不会处理后台工作人员的“已完成”通知,因此状态将保持忙碌状态。

答案 4 :(得分:1)

我遇到了类似的问题,而我要解决的问题是用main try... catch... and finally...语句包含main函数。

答案 5 :(得分:0)

.IsBusy仅表示后台工作人员正在执行操作。它会很忙,直到“某事”完成。似乎“某事”没有完成,让你的背景工作者忙碌。

因此,如果您可以解释什么是“某事”,并且可能需要多长时间在主线程上执行“某事”,这将有所帮助。

答案 6 :(得分: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();
        }
    }
}

答案 7 :(得分:-1)

以下示例给出了针对您的问题的解决方案。就像Hans Passant解释的那样,你的代码在后台工作程序中运行,但每个线程的RunWorkerCompleted只在ui Thread中进行评估。为此,您可以在线程的UI队列(ThreadPool)中排队您的请求。这样,UI会评估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();
    }        
}