Reputation: 1125
I would like to understand how BackgroundWorker
is being used.
I have tried to break it down to a Console C# project, actually it is a Windows Form Application, where a button triggers the execution of three background tasks. When the button is pressed, it should become disabled preventing further button events. When all three tasks have finished, the button should be enabled again. Also the success of the three tasks should be tested in the main thread. To prevent from mixing this all into a Forms app, I'm trying now to understand the basics and move that to the Forms application.
(From the comments settings you may guess where my problems of understanding are)
Consider this (still erroneous) code:
using System;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
namespace BGWorkerConsoleApp
{
class Program
{
// #region BackgroundWorker
//
private System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
//
// #endregion
static void Main(string[] args)
{
// BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
worker.RunWorkerCompleted += TestComplete;
worker.DoWork += TestConnection1;
// worker.DoWork += TestConnection2;
// worker.DoWork += TestConnection3;
DoWorkEventArgs e = new DoWorkEventArgs(); // ???
worker.RunWorkerAsync(e); // ???
}
private void TestConnection1(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(14000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestConnection2(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(10000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestConnection3(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(5000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestComplete(object sender, RunWorkerCompletedEventArgs e)
{
Console.WriteLine("complete");
}
}
}
My questions:
Can I add more than one Task to a BackgroundWorker
? Probably not, since there is only one RunWorkerAsync. Assuming I need a worker for every task, how would I wait for all three tasks to complete?
OK, let me change it to a Forms application since the stress doesn't lay on running BackGroundworker in a console App, rather I would like to understand how to design the application for more than one BackGroundWorkers and wait for their completion (including the passing and returning of parameters and results.
Here is the code for the same in a Form app:
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Threading;
namespace BackGroundWorkerForm
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
private Button button;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Text = "Form1";
button = new System.Windows.Forms.Button();
button.Location = new System.Drawing.Point(100, 10);
button.Name = "button";
button.Size = new System.Drawing.Size(100, 20);
button.TabIndex = 5;
button.Text = "TestConnection";
button.Click += new System.EventHandler(this.RunTest);
button.Visible = true;
Controls.Add(button);
}
#endregion
private void RunTest(object o, EventArgs e)
{
BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
worker.RunWorkerCompleted += TestComplete;
worker.DoWork += TestConnection1;
// worker.DoWork += TestConnection2;
// worker.DoWork += TestConnection3;
worker.RunWorkerAsync();
}
private void TestConnection1(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(10000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestConnection2(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(10000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestConnection3(object sender, DoWorkEventArgs e)
{
bool success = false;
Thread.Sleep(10000); // stands for some time consuming Task
success = true;
e.Result = success;
}
private void TestComplete(object sender, RunWorkerCompletedEventArgs e)
{
button.Text= "complete";
}
}
}
Upvotes: 3
Views: 3558
Reputation: 5204
Here is an example using Tasks instead of a background worker. Each Task is started asynchronously, and then it waits for all to complete successfully or not before processing the results.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace BGWorkerConsoleApp
{
class Program
{
static Stopwatch sw = new Stopwatch();
static void Main(string[] args)
{
// parse your args
int[] parsedargs = { 1400, 1000, 500 };
int count = 0; // to provide task count
List<Task> tasks = new List<Task>();
sw.Start(); //stopwatch for some to verify the time
foreach (int i in parsedargs)
{
// start a task for each
tasks.Add(Task.Factory.StartNew<bool>(
() => { return myTask(i, String.Format("Task{0} done. ", ++count)); } ) );
}
// wait for all the tasks to complete
Task.WaitAll(tasks.ToArray());
// check the response of each
bool faulted = false;
foreach (Task<bool> t in tasks)
{
if (t.Result == false)
{
faulted = true;
break; //there was a problem so quit looking
}
}
//output some text
if (faulted)
{
Console.WriteLine("There was a problem.");
}
else
Console.WriteLine("complete");
// pause so we can see our output in the debugger
Console.ReadKey();
}
static bool myTask(int time, string msg)
{
Thread.Sleep(time);
if (time == 1000)
return false;
Console.WriteLine(msg + printStopWatchTime());
return true;
}
static string printStopWatchTime()
{
TimeSpan ts = sw.Elapsed;
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds,
ts.Milliseconds / 10);
return string.Format("RunTime {0}", elapsedTime);
}
}
}
Upvotes: 1
Reputation: 56717
A BackgroundWorker
is good if you want to execute one specific task in the background. That is, the DoWork
event is assigned one handler that performs the task and finishes. Of course you can change the event method by removing the old one and assigning a new one, but the important part is that only one task can be performed at a time (you can not have the same background worker run more than once at the same time).
If you want to have two or more tasks to be performed at the same time, you either need more background workers, or you start looking into the Task Parallel Library, as suggested in the comments. Even plain Thread
s would work.
In either case, as Leandro said in his answer, what you're trying to achive screams for the use of some barrier, as otherwise the first finishing task will enable the button before the other tasks are finished. You, however, want to wait until all the tasks are finished.
I would like to add that in my opinion, use cases for the BackgroundWorker
are pretty limited. While it is convenient in some cases, it does not provide the flexibility needed in most cases.
Upvotes: 0
Reputation: 1555
For your first question: you can reuse a BackgroundWorker
as long as you don't try to run the task while it's already runnig (i.e.: IsBusy
must equal false
). According to MSDN, if you try to do that, it will bite you hard with an InvalidOperationException
.
On the second matter: you need some kind of synchronization mechanism to accomplish that. Check WaitHandle.WaitAll(...) and the Barrier class, for instance. There are plenty of options, not just those. You have monitors, semaphores, mutexes and whatnot at your disposal. Do explore the System.Threading
namespace.
Upvotes: 0