From: thezim on
I'm trying to figure out why the following doesn't work. It hang
waiting for a BackGroundWorker to become "un busy". With the 11 item
in the queue the first 10 get to the DoWork but never to RunCompleted.
If the items in the queue are equal to MaxWorker then everything works
okay.

I am goin insane. I knows its some hidden inherent way im doing
something that i have yet to see documented or maybe im just in over
my head.



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Diagnostics;

namespace WindowsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private BackgroundWorker[] Workers;
private int MaxWorkers = 10;

private void button1_Click(object sender, EventArgs e)
{
Queue<string> Clients = new Queue<string>();

Clients.Enqueue("TEST01");
Clients.Enqueue("TEST02");
Clients.Enqueue("TEST03");
Clients.Enqueue("TEST04");
Clients.Enqueue("TEST05");
Clients.Enqueue("TEST06");
Clients.Enqueue("TEST07");
Clients.Enqueue("TEST08");
Clients.Enqueue("TEST09");
Clients.Enqueue("TEST10");
Clients.Enqueue("TEST11");

Workers = new BackgroundWorker[MaxWorkers];
for (int i = 0; i < MaxWorkers; i++)
{
Workers[i] = new BackgroundWorker();
Workers[i].WorkerSupportsCancellation = true;
Workers[i].DoWork += new
DoWorkEventHandler(bw_DoWork);
Workers[i].RunWorkerCompleted += new
RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
while (Clients.Count != 0)
{
for (int i = 0; i < 10; i++)
{
if (!Workers[i].IsBusy)
{
string hostname = Clients.Dequeue();
Debug.WriteLine("Starting " + hostname);
Workers[i].RunWorkerAsync(hostname);
}
}
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
Debug.WriteLine("Working " + e.Argument.ToString());
e.Result = e.Argument.ToString();
}
private void bw_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
Debug.WriteLine("Completed " + e.Result);
}
}
}
From: Peter Duniho on
On Tue, 11 Dec 2007 18:51:15 -0800, thezim <thezim(a)gmail.com> wrote:

> I'm trying to figure out why the following doesn't work. It hang
> waiting for a BackGroundWorker to become "un busy". With the 11 item
> in the queue the first 10 get to the DoWork but never to RunCompleted.
> If the items in the queue are equal to MaxWorker then everything works
> okay.
>
> I am goin insane. I knows its some hidden inherent way im doing
> something that i have yet to see documented or maybe im just in over
> my head.

The main problem is that one of the main points of using the
BackgroundWorker class is that the RunWorkerCompleted event is raised on
the same thread that created the BackgroundWorker instance, but you have
blocked the raising of any events on your main thread by never returning
from the Click event handler.

The fact that you're polling the BackgroundWorker list is bad, and you
have a potential crashing bug waiting to happen because you don't check
for an empty queue before trying to dequeue a new item for working.

But the main problem is that you are stuck in your Click handler,
preventing the RunWorkerCompleted events from being raised.

The fix would be to move the code that enqueues new work items from the
click handler to the RunWorkerCompleted event. Actually, that's a fix for
at least two problems: the one where your code doesn't return from the
Click handler, as well as the problem where your code is polling the list
of BackgroundWorkers.

I've posted a fixed version of your program below. The technique of using
the sender to start a new background task in the RunWorkerCompleted event
also serendipitously fixes the problem that each time the button is
clicked a new array of BackgroundWorkers is created (but there remains the
problem that you never dispose the BackgroundWorker instances...hopefully
both problems are more related to the demonstration nature of the code,
rather than something you'd include in a real program :) ).

Hope that helps.

Pete


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;

namespace TestQueueBGWorker
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private BackgroundWorker[] Workers;
private int MaxWorkers = 10;
private Queue<string> Clients = new Queue<string>();

private void button1_Click(object sender, EventArgs e)
{
Clients.Enqueue("TEST01");
Clients.Enqueue("TEST02");
Clients.Enqueue("TEST03");
Clients.Enqueue("TEST04");
Clients.Enqueue("TEST05");
Clients.Enqueue("TEST06");
Clients.Enqueue("TEST07");
Clients.Enqueue("TEST08");
Clients.Enqueue("TEST09");
Clients.Enqueue("TEST10");
Clients.Enqueue("TEST11");

Workers = new BackgroundWorker[MaxWorkers];
for (int i = 0; i < MaxWorkers; i++)
{
Workers[i] = new BackgroundWorker();
Workers[i].WorkerSupportsCancellation = true;
Workers[i].DoWork += new DoWorkEventHandler(bw_DoWork);
Workers[i].RunWorkerCompleted += new
RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
_StartNewClient(Workers[i]);
}

Debug.WriteLine("Finished Click handler");
}

private void _StartNewClient(BackgroundWorker bw)
{
if (Clients.Count > 0)
{
string hostname = Clients.Dequeue();
Debug.WriteLine("Starting " + hostname);
bw.RunWorkerAsync(hostname);
}
}

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
Debug.WriteLine("Working " + e.Argument.ToString());
Thread.Sleep(1000);
e.Result = e.Argument.ToString();
}

private void bw_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
Debug.WriteLine("Completed " + e.Result);
_StartNewClient((BackgroundWorker)sender);
}
}
}
From: Peter Duniho on
On Tue, 11 Dec 2007 20:46:36 -0800, Peter Duniho
<NpOeStPeAdM(a)nnowslpianmk.com> wrote:

> [...]
> I've posted a fixed version of your program below.

And just in case it wasn't clear: the call to Thread.Sleep() is definitely
not necessary, and you wouldn't put it into a working application. I
added it as part of testing the new version, and just didn't bother
removing it before posting the new sample.

Pete