I have already asked a somwhat similar question here but I now have a follow-up question.
I need to launch the external program several times in a row, but I have se
So nobugz give you already the right direction, but for completeness here is some sample code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace Threading
{
public partial class FormMain : Form
{
private BackgroundWorker _BackgroundWorker;
private Queue<Func<string>> _Commands;
private Random _Random;
public FormMain()
{
InitializeComponent();
_Random = new Random();
_Commands = new Queue<Func<string>>();
_BackgroundWorker = new BackgroundWorker();
_BackgroundWorker.WorkerReportsProgress = true;
_BackgroundWorker.WorkerSupportsCancellation = true;
_BackgroundWorker.DoWork += backgroundWorker_DoWork;
_BackgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;
_BackgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
_BackgroundWorker.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (!_BackgroundWorker.CancellationPending)
{
if (_Commands.Count > 0)
{
AddMessage("Starting waiting job...");
AddMessage(_Commands.Dequeue().Invoke());
}
Thread.Sleep(1);
}
}
void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
AddMessage("BackgroundWorker doesn't make any further jobs.");
}
private void buttonStart_Click(object sender, EventArgs e)
{
_Commands.Enqueue(DoSomething);
//or maybe with a lambda
//_Commands.Enqueue(new Func<string>(() =>
//{
// string message;
// message = DoSomething();
// return message;
//}));
}
private string DoSomething()
{
int max = 10;
for (int i = 1; i <= max; i++)
{
Thread.Sleep(_Random.Next(10, 1000));
if (_BackgroundWorker.CancellationPending)
{
return "Job aborted!";
}
AddMessage(String.Format("Currently working on item {0} of {1}", i, max));
_BackgroundWorker.ReportProgress((i*100)/max);
}
return "Job is done.";
}
private void AddMessage(string message)
{
if (textBoxOutput.InvokeRequired)
{
textBoxOutput.BeginInvoke(new Action<string>(AddMessageInternal), message);
}
else
{
AddMessageInternal(message);
}
}
private void AddMessageInternal(string message)
{
textBoxOutput.AppendText(String.Format("{0:G} {1}{2}", DateTime.Now, message, Environment.NewLine));
textBoxOutput.SelectionStart = textBoxOutput.Text.Length;
textBoxOutput.ScrollToCaret();
}
private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
{
if (_BackgroundWorker.IsBusy)
{
_BackgroundWorker.CancelAsync();
e.Cancel = true;
AddMessage("Please close only if all jobs are done...");
}
}
}
}
The fact that you have a while (bgwkSVN.IsBusy) { }
in your main form thread is why your form stops responding. The background worker is executing it's work on a separate thread but your UI thread is blocked. You should consider starting one RunWorkerAsync() method in the MergerRevisions
call and then start the next in the bgwkSVN.RunWorkerCompleted
event.
If you're looking for a nasty quick fix that is the wrong way to do it here it is:
Change:
while (bgwkSVN.IsBusy) { }
To:
while (bgwkSVN.IsBusy)
{
System.Threading.Thread.Sleep(1000); // Make the current (UI/Form) thread sleep for 1 second
Application.DoEvents();
}
The solution is simple: have one BGW execute all of the commands, not just one BGW for each command. You'll need a List<svnCommand>
to store the commands so you can easily pass them to RunWorkerAsync(). DoWork() can simply iterate the list with foreach.
the while (bgwkSVN.IsBusy) { }
is waiting in a tight loop and looks like it's causing your delays. I'd split the process into several background threads and start the 'next' one in backgroundWorkerX_RunWorkerCompleted.