I am noticing some strange behaviour with BackgroundWorkers and the events they're firing where the events seem to be queuing up in one thread while the CPU isn't actually being used.
Basically the design of the system is that, based on user interaction, a thread is created to send off a web request to fetch some data. Based on the results, it may fire off many other asynchronous requests, using BackgroundWorkers for each of these. I am doing this because the code that manages the requests uses a lock to ensure only one request is being sent at a time (to avoid spamming the server with multiple simultaneous requests, possibly leading to the server ignoring / blocking them). There may be a better design to this, which I'd love to hear (I'm relatively new to C# / Windows Forms programming and could use the advice). However, regardless of the design changes I'm interested to learn what's causing the behaviour I'm seeing.
I've written a relatively simple test app to demonstrate the issue. It's basically just a form with a button and a text box to display the results (you could probably do it without the form and just display results on the console, but I did it this way to replicate what my actual app does). Here's the code:
delegate void AddToLogCallback(string str);
private void AddToLog(string str)
{
if(textBox1.InvokeRequired)
{
AddToLogCallback callback = new AddToLogCallback(AddToLog);
Invoke(callback, new object[] { str });
}
else
{
textBox1.Text += DateTime.Now.ToString() + " " + str + System.Environment.NewLine;
textBox1.Select(textBox1.Text.Length, 0);
textBox1.ScrollToCaret();
}
}
private void Progress(object sender, ProgressChangedEventArgs args)
{
AddToLog(args.UserState.ToString());
}
private void Completed(object sender, RunWorkerCompletedEventArgs args)
{
AddToLog(args.Result.ToString());
}
private void DoWork(object sender, DoWorkEventArgs args)
{
BackgroundWorker worker = sender as BackgroundWorker;
lock (typeof(Form1)) // Ensure only a single request at a time
{
worker.ReportProgress(0, "Start");
Thread.Sleep(2000); // Simulate waiting on the request
worker.ReportProgress(50, "Middle");
Thread.Sleep(2000); // Simulate handling the response from the request
worker.ReportProgress(100, "End");
args.Result = args.Argument;
}
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(RunMe);
thread.Start();
}
private void RunMe()
{
for(int i=0; i < 20; i++)
{
AddToLog("Starting " + i.ToString());
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += DoWork;
worker.RunWorkerCompleted += Completed;
worker.ProgressChanged += Progress;
worker.RunWorkerAsync(i);
}
}
Here are the results I'm getting back:
30/07/2009 2:43:22 PM Starting 0
30/07/2009 2:43:22 PM Starting 1
<snip>
30/07/2009 2:43:22 PM Starting 18
30/07/2009 2:43:22 PM Starting 19
30/07/2009 2:43:23 PM Start
30/07/2009 2:43:36 PM Middle
30/07/2009 2:43:36 PM End
30/07/2009 2:43:36 PM 0
30/07/2009 2:43:36 PM Start
30/07/2009 2:43:36 PM Middle
30/07/2009 2:43:36 PM End
30/07/2009 2:43:36 PM 1
30/07/2009 2:43:36 PM Start
30/07/2009 2:43:36 PM Middle
30/07/2009 2:43:36 PM End
30/07/2009 2:43:36 PM 8
30/07/2009 2:43:36 PM Start
30/07/2009 2:43:36 PM Middle
30/07/2009 2:43:38 PM 13
30/07/2009 2:43:38 PM End
30/07/2009 2:43:38 PM Start
30/07/2009 2:43:40 PM Middle
30/07/2009 2:43:42 PM 18
30/07/2009 2:43:42 PM Start
30/07/2009 2:43:42 PM End
30/07/2009 2:43:44 PM Middle
30/07/2009 2:43:46 PM End
30/07/2009 2:43:46 PM 2
30/07/2009 2:43:46 PM Start
30/07/2009 2:43:48 PM Middle
As you can see, there is a 13 second delay after the first 'Start' message is displayed, after which it then processes ~15 messages (despite there being a 2s delay between most of them being fired).
Anyone know what's going on?