views:

621

answers:

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

it runs five times (every BG-worker once) and gets stuck in the while. Don't the backgroundworkers ever stop being busy ? how do I check availability ?

note: there are 5 worker threads, this assures none of them is ever stopped, always assigning work to them. But they refuse to tell me when they are available, I thought that would have a simple solution..

--[edit request]---

Actually it was only a dummy parameter, i removed it and forgot to get it out, I only use it to call the dowork, who does the dirty job:

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

And the timeconsumingfunction DOES end. stepping into it in the debugger and running line per line, it goes until the end and arrives in the final '}'. That means it ends, right ?

---[EDIT ANSWER]---- it worked by JUST replacing the line

System.Threading.Thread.Sleep(0001);

with

Application.DoEvents();

I guess it would run the background, but not receive the answer and not update the IsBusy tags.

Thanks everybody, great answers, helped a lot!

A: 

The .IsBusy only indicates that the backgroundworker is actually performing an operation. It will be busy until "something" is completed. It seems that "something" doesn't finish, keeping your backgroundworkers busy.

So it would help if you could explain what "something" is, and possibly how long it takes to perform "something" on the main thread.

Onots
+3  A: 

I suggest you change your code to handle the RunWorkerCompleted event to get notifications when your BackgroundWorkers are finished with their work. There is an example of how to use the BackgroundWorker in the official documentation.

Anders Fjeldstad
+1  A: 

I had this same problem whilst using background workers and came the conclusion that if you use sleep() within a loop, then it will get stuck. You can either use the RunWorkerCompleted event and set a boolean flag to indicate when each worker is finished.

Or if you want to abort the thread regardless you could look at using threads instead of background workers. However then you lose the ease of use in regards to the events which the background worker provides.

53an
+7  A: 

Your loop is causing deadlock, the BGWs cannot complete. The problem is the RunWorkerCompleted event, it is raised on the UI thread. That bit of BGW magic requires the UI thread to be idle, it must be pumping its message loop. Problem is, the UI thread is not idle and it isn't pumping messages, it is stuck in the for loop. Thus, the event handler cannot run and IsBusy stays true.

You'll need to do this differently. Leverage the RunWorkerCompleted event to run the code that you'd normally run after this for loop. Resist all temptation to call Application.DoEvents() inside the loop.

Hans Passant
It's almost like an eternal for, and nothing runs after it. I am checking website availabilities in an array (using the for's index), so there's no way to just "continue" in the RunWorkerCompleted. If you have any sugestions, please, edit your answer.. I'm clueless :(
MarceloRamires
And it DOES complete. I've followed it with the debugger, and it arrives to the "}" at a given point when there has been some sleep(0001) which isn't as much as in runtime, because there is time between step in's, so i sleep(0001) about 20 times... then it runs again, goes until the '}', but even after the last "}" of the doWork, the BGW won't change the isBusy to false.
MarceloRamires
No, it isn't complete until the RunWorkerCompleted event finished running. Which happens after the DoWork event handler completed. The RWC handler is the problem, it can't run because your UI thread is stuck in a loop. Removing the handler would be a quick fix.
Hans Passant
you told me to resist putting Application.DoEvents() in the loop... why ?
MarceloRamires
Because your program will crash and burn when the user closes the form while your loop is running. Work around that by setting the form's Enable property to false before you enter the loop.
Hans Passant
+1  A: 

Your main thread needs to be pumping Windows messages (either calling Application.DoEvents in your while loop, or better still by using a Systems.Windows.Forms.Timer instead of a loop).

If you don't pump Windows messages, your background worker's "completed" notifications won't be processed, so the status will remain busy.

Joe
A: 

The problem is that whatever you're doing within worker.RunWorkerAsync() never gets finished. Maybe there is some endless loop or something like that defined in your DoWork event.

Here is a working example, that selects the next free worker:

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();
        }
    }
}
Oliver
A: 

I had a similar problem, and what i did to solve it was to enclose the main function with a try .. catch .. and finally .. statement.

CP Net