Observe the following piece of code:
var handler = GetTheRightHandler();
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += OnAsyncOperationCompleted;
BackgroundWorker has a completion event. Instead of waiting, call your remaining code path from the completion handler.
To wait for a background worker thread (single or multiple) do the following:
Create a List of Background workers you have programatically created:
private IList<BackgroundWorker> m_WorkersWithData = new List<BackgroundWorker>();
Add the background worker in the list:
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.WorkerReportsProgress = true;
m_WorkersWithData.Add(worker);
worker.RunWorkerAsync();
Use the following function to wait for all workers in the List:
private void CheckAllThreadsHaveFinishedWorking()
{
bool hasAllThreadsFinished = false;
while (!hasAllThreadsFinished)
{
hasAllThreadsFinished = (from worker in m_WorkersWithData
where worker.IsBusy
select worker).ToList().Count == 0;
Application.DoEvents(); //This call is very important if you want to have a progress bar and want to update it
//from the Progress event of the background worker.
Thread.Sleep(1000); //This call waits if the loop continues making sure that the CPU time gets freed before
//re-checking.
}
m_WorkersWithData.Clear(); //After the loop exits clear the list of all background workers to release memory.
//On the contrary you can also dispose your background workers.
}
This question is old but I don't think the author got the answer he was looking for.
This is a bit dirty, and it's in vb.NET but works for me
Private Sub MultiTaskingForThePoor()
Try
'Start background worker
bgwAsyncTasks.RunWorkerAsync()
'Do some other stuff here
For i as integer = 0 to 100
lblOutput.Text = cstr(i)
Next
'Wait for Background worker
While bgwAsyncTasks.isBusy()
Windows.Forms.Application.DoEvents()
End While
'Voila, we are back in sync
lblOutput.Text = "Success!"
Catch ex As Exception
MsgBox("Oops!" & vbcrlf & ex.Message)
End Try
End Sub
Checking backgrWorker.IsBusy
in the loop with Application.DoEvents()
is not a nicely way.
I agree with @JohannesH, you should definitively use AutoResetEvent as a elegant solution. But not using it in UI Thread, it will cause main thread blocked; it should come from another background worker thread.
AutoResetEvent aevent = new AutoResetEvent(false);
private void button1_Click(object sender, EventArgs e)
{
bws = new BackgroundWorker();
bws.DoWork += new DoWorkEventHandler(bw_work);
bws.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_complete);
bws.RunWorkerAsync();
bwWaiting.DoWork += new DoWorkEventHandler(waiting_work);
bwWaiting.RunWorkerCompleted += new RunWorkerCompletedEventHandler(waiting_complete);
bwWaiting.RunWorkerAsync();
}
void bw_work(object sender, DoWorkEventArgs e)
{
Thread.Sleep(2000);
}
void bw_complete(object sender, RunWorkerCompletedEventArgs e)
{
Debug.WriteLine("complete " + bwThread.ToString());
aevent.Set();
}
void waiting_work(object sender, DoWorkEventArgs e)
{
aevent.WaitOne();
}
void waiting_complete(object sender, RunWorkerCompletedEventArgs e)
{
Debug.WriteLine("complete waiting thread");
}
While BackgroundWorker1.IsBusy()
Windows.Forms.Application.DoEvents()
End While
You can use this to chain multiple events. (sudo code to follow)
download_file("filepath")
While BackgroundWorker1.IsBusy()
Windows.Forms.Application.DoEvents()
End While
'Waits to install until the download is complete and lets other UI events function install_file("filepath")
While BackgroundWorker1.IsBusy()
Windows.Forms.Application.DoEvents()
End While
'Waits for the install to complete before presenting the message box
msgbox("File Installed")
I was also looking for a suitable solution. I solved the waiting with an exclusive lock. The critical path in code are writing to a public container (here the console) and increasing or decreasing the workers. No thread should interfere while writing to this variable, otherwise the count is not guaranteed anymore.
public class Program
{
public static int worker = 0;
public static object lockObject = 0;
static void Main(string[] args)
{
BackgroundworkerTest backgroundworkerTest = new BackgroundworkerTest();
backgroundworkerTest.WalkDir("C:\\");
while (backgroundworkerTest.Worker > 0)
{
// Exclusive write on console
lock (backgroundworkerTest.ExclusiveLock)
{
Console.CursorTop = 4; Console.CursorLeft = 1;
var consoleOut = string.Format("Worker busy count={0}", backgroundworkerTest.Worker);
Console.Write("{0}{1}", consoleOut, new string(' ', Console.WindowWidth-consoleOut.Length));
}
}
}
}
public class BackgroundworkerTest
{
private int worker = 0;
public object ExclusiveLock = 0;
public int Worker
{
get { return this.worker; }
}
public void WalkDir(string dir)
{
// Exclusive write on console
lock (this.ExclusiveLock)
{
Console.CursorTop = 1; Console.CursorLeft = 1;
var consoleOut = string.Format("Directory={0}", dir);
Console.Write("{0}{1}", consoleOut, new string(' ', Console.WindowWidth*3 - consoleOut.Length));
}
var currentDir = new System.IO.DirectoryInfo(dir);
DirectoryInfo[] directoryList = null;
try
{
directoryList = currentDir.GetDirectories();
}
catch (UnauthorizedAccessException unauthorizedAccessException)
{
// No access to this directory, so let's leave
return;
}
foreach (var directoryInfo in directoryList)
{
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += (sender, args) =>
{
// Make sure that this worker variable is not messed up
lock (this.ExclusiveLock)
{
worker--;
}
};
DirectoryInfo info = directoryInfo;
bw.DoWork += (sender, args) => this.WalkDir(info.FullName);
lock (this.ExclusiveLock)
{
// Make sure that this worker variable is not messed up
worker++;
}
bw.RunWorkerAsync();
}
}
}