I am trying to create a new Thread and put it to sleep in some occasions, but when I do it the main thread sleep, not only the one that I had created. I am using a Dispatcher.BeginInvoke but this is only to "give a permission" from the main thread to access to the method.
It works because it does not give me an InvalidOperationException, but the "focus" of the created thread losses when the linked method start.
I think I should use a ManualResetEvent to wait for the created Thread, but I do not know how doing it. I have been looking for possible solutions but no one works.
I think this should be easy but i cannot do it. The following code is Below:
void EmpujeDispatcher(object objeto)
{
this.Dispatcher.BeginInvoke(new Action<object>(Empuje), objeto);
}
private void Empuje(object objeto)
{
Thread.Sleep(2000); MessageBox.Show("This should not freeze the window");
Canvas Bacteria = objeto;
double PosX = Canvas.GetLeft(Bacteria);//PosiciĆ³n del sender
double PosY = Canvas.GetTop(Bacteria);//Lo mismo
Bacterias BacteriaInstancia = InstanciaBacterias[Bacteria.Uid];//Se busca la bacteria para relacionarla con al instancia
BacteriaInstancia.posX = PosX;
BacteriaInstancia.posY = PosY;
// BacteriaInstancia.Moverse();
if (BacteriaInstancia.momemtum <= 0)
{
Canvas.SetTop(Bacteria, PosY); Canvas.SetLeft(Bacteria, PosX);//Para el empuje
dispatcherTimer.Stop();
}
else
{ //Rebote:
BacteriaInstancia.Posicion();
PosX = BacteriaInstancia.posX;
PosY = BacteriaInstancia.posY;
if (PosX + Bacteria.Width >= CanvasSimulador.Width) { BacteriaInstancia.direccionAnterior = BacteriaInstancia.direccion; BacteriaInstancia.direccion = 1; }
if (PosX <= 0) { BacteriaInstancia.direccionAnterior = BacteriaInstancia.direccion; BacteriaInstancia.direccion = 3; }
if (PosY + Bacteria.Height >= CanvasSimulador.Height) { PosY = CanvasSimulador.Height - Bacteria.Height; BacteriaInstancia.direccionAnterior = BacteriaInstancia.direccion; BacteriaInstancia.direccion = 2; }
if (PosY <= 0) { PosY = 1; BacteriaInstancia.direccionAnterior = BacteriaInstancia.direccion; BacteriaInstancia.direccion = 4; }
Canvas.SetTop(Bacteria, PosY); Canvas.SetLeft(Bacteria, PosX);
BacteriaInstancia.momemtum = Math.Sqrt(Math.Pow(BacteriaInstancia.Vfx, 2) + Math.Pow(BacteriaInstancia.Vfy, 2));
ControlFlujo = BacteriaInstancia.momemtum;
}
private void EmpujeEvent(object sender, MouseButtonEventArgs e)
{
Thread TimerClockThread = new Thread(new ParameterizedThreadStart(EmpujeDispatcher));
TimerClockThread.IsBackground = true;
TimerClockThread.Start(sender);
}
This is not exacly the code because in this one Dispatcher does not have any sense, if I create the Thread without dispatcher
TimerClockThread = new Thread( new ParameterizedThreadStart(Empuje));
It works well... because it's a MessageBox, but in the original I have a lot of code inside of the "Empuje".
Thanks for your attention and hopefully you can help me :)
Your Dispatcher.Invoke forces your Empuje method to be called on the UI thread. If you want to update the screen, you should move the call to the background thread:
TimerClockThread = new Thread( new ParameterizedThreadStart(Empuje));
private void Empuje(object objeto)
{
Thread.Sleep(2000);
Dispatcher.BeginInvoke(new Action(() => {
MessageBox.Show("This should not freeze the window");
}));
//........ Do stuff.....
}
In modern C# with async however, you can remove all code and instead write:
private async void EmpujeEvent(object sender, MouseButtonEventArgs e)
{
await Task.Delay(2000);
MessageBox.Show(...);
}
Your function EmpujeDispatcher is using the same dispatcher that your GUI thread is associated with. That means you are telling the dispatcher to execute Empuje asynchronously, unfortunately it is executed on the GUI thread. At least that's what I think.
A BackgroundWorker is quite fit for this task. The shortest piece of code to implement it looks like this:
var worker = new BackgroundWorker();
worker.DoWork += (s,e) =>
{
Thread.Sleep(2000);
// Do Stuff...
};
worker.RunWorkerAsync();
Searching SO will yield a plethora of Q&A about the BackgroundWorker (i.e. this or this)
Related
I tried to dive into multithreading once again and hit the ground quite fast (.Net 4.5).
I have a class library, which does some heavy byte-reading from files, raising an event.
public class cMyClass
{
public event EventHandler<cUpdateEventArgs> OnReadUpdate = delegate{ };
public int id {get; private set;}
public cMyClass(int in_id)
{ id = in_id; }
public async void ReadFromFile(string in_filePath)
{ // Do a bit of reading all bytes here and error-checking there...
//Here comes the heavy lifting
await ReadTriangles(stlBytes.SubArray(cConstants.BYTES_IN_HEADER, stlBytes.Length - cConstants.BYTES_IN_HEADER));
}
private Task ReadTriangles(byte[] in_triangles)
{
UInt32 numberOfTriangles = BitConverter.ToUInt32(cHelpers.HandleLSBFirst(in_triangles.SubArray(0, 4)), 0);
float percentage = 0;
float percentageOld = percentage;
OnReadUpdate(this, new cReadUpdateEventArgs(id, Resources.Texts.ReadingTriangles, percentage));
for (UInt32 i = 0; i < numberOfTriangles; i++)
{
percentage = ((float)(i + 1)) / numberOfTriangles * 100;
triangleList.Add(new cSTLTriangle(in_triangles.SubArray(Convert.ToInt32(i * cConstants.BYTES_PER_TRIANGLE + 4), Convert.ToInt32(cConstants.BYTES_PER_TRIANGLE))));
if (percentage - percentageOld >= 0.1) //Just tell about .1-percentage increases
{
percentageOld = percentage;
OnReadUpdate(this, new cReadUpdateEventArgs(id, Resources.Texts.ReadingTriangles, percentage));
}
}
OnReadUpdate(this, new cReadUpdateEventArgs(id, Resources.Texts.ReadingTriangles, percentage));
return Task.FromResult<bool>(true);
}
}
As you can see, any instance of cMyClass would run the ReadFromFile Task on a different thread. So naturally - wanting to be thread-safe - I have to do some Invoking. But wait!
Moving on to a WindowsForms-Application is where things start to go wrong. I have a testing form with just two progressBars and a button.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
progressBar1.Minimum = 0;
progressBar1.Maximum = 1000;
progressBar2.Minimum = 0;
progressBar2.Maximum = 1000;
//Now the same code as from the console application:
cSTLBinaryDataModel stlFile = new cSTLBinaryDataModel(1);
cSTLBinaryDataModel stlFile2 = new cSTLBinaryDataModel(2);
stlFile.OnReadUpdate += stlFile_OnReadUpdate;
stlFile2.OnReadUpdate += stlFile_OnReadUpdate;
List<Task> taskList = new List<Task>();
taskList.Add(new Task(new Action(() => stlFile.ReadFromFile(#"C:\temp\Test.stl"))));
taskList.Add(new Task(new Action(() => stlFile2.ReadFromFile(#"C:\temp\Test.stl"))));
foreach (Task t in taskList)
t.Start();
// I need Task.WaitAll to re-enable the button after all processing is done
Task.WaitAll(taskList.ToArray());
button1.Enabled = true;
}
private void stlFile_OnReadUpdate(object sender, cReadUpdateEventArgs e)
{
switch (e.id)
{
case 1:
progressBar1.BeginInvoke(new Action(() =>
progressBar1.Value = Convert.ToInt32(e.percentage * 10)));
break;
case 2:
progressBar2.BeginInvoke(new Action(() =>
progressBar2.Value = Convert.ToInt32(e.percentage * 10)));
break;
}
}
}
This works in so far, that the files are being processed in the background (I checked the Task manager) but the Task.WaitAll blocks my UI, so after everything is done, I can see the progressBars running up.
How can I wait for the Tasks to finish but at the same time keep the UI responsive?
I tried to dive into multithreading once again...
Multithreading is totally old-school. These days, for I/O-bound opeations, use async/await and the patterns that go along with it (i.e., IProgress<T> for progress updates). You'll find your life a lot easier.
These principles will help:
Write synchronous code if the code is synchronous, and only use async/await if the code is asynchronous. In the code you posted, ReadTriangles should not have an asynchronous signature.
Get rid of the event. Cross-thread events are messy. Instead, use IProgress<T>.
Avoid async void; use async Task instead.
Here's how your class would look with these principles in place:
public async Task ReadFromFileAsync(string in_filePath, IProgress<cUpdateEventArgs> progress)
{
// Do a bit of reading all bytes here and error-checking there...
//Here comes the heavy lifting
ReadTriangles(stlBytes.SubArray(cConstants.BYTES_IN_HEADER, stlBytes.Length - cConstants.BYTES_IN_HEADER), progress);
}
private void ReadTriangles(byte[] in_triangles, IProgress<cUpdateEventArgs> progress)
{
UInt32 numberOfTriangles = BitConverter.ToUInt32(cHelpers.HandleLSBFirst(in_triangles.SubArray(0, 4)), 0);
float percentage = 0;
float percentageOld = percentage;
if (progress != null)
progress.Report(new cReadUpdateEventArgs(id, Resources.Texts.ReadingTriangles, percentage));
for (UInt32 i = 0; i < numberOfTriangles; i++)
{
percentage = ((float)(i + 1)) / numberOfTriangles * 100;
triangleList.Add(new cSTLTriangle(in_triangles.SubArray(Convert.ToInt32(i * cConstants.BYTES_PER_TRIANGLE + 4), Convert.ToInt32(cConstants.BYTES_PER_TRIANGLE))));
if (percentage - percentageOld >= 0.1) //Just tell about .1-percentage increases
{
percentageOld = percentage;
if (progress != null)
progress.Report(new cReadUpdateEventArgs(id, Resources.Texts.ReadingTriangles, percentage));
}
}
if (progress != null)
progress.Report(new cReadUpdateEventArgs(id, Resources.Texts.ReadingTriangles, percentage));
}
And here's one way you could use it. Note the use of Task.Run in the UI layer to push the CPU-bound work off the UI thread:
cSTLBinaryDataModel stlFile = new cSTLBinaryDataModel(1);
cSTLBinaryDataModel stlFile2 = new cSTLBinaryDataModel(2);
var progress = new Progress<cReadUpdateEventArgs>(update =>
{
switch (update.id)
{
case 1:
progressBar1.Value = Convert.ToInt32(update.percentage * 10);
break;
case 2:
progressBar2.Value = Convert.ToInt32(update.percentage * 10);
break;
}
});
await Task.WhenAll(
Task.Run(() => stlFile.ReadFromFileAsync(#"C:\temp\Test.stl", progress)),
Task.Run(() => stlFile2.ReadFromFileAsync(#"C:\temp\Test.stl", progress)));
button1.Enabled = true;
Of course the UI doesn't update if you call Task.WaitAll. This stops the UI thread until all tasks are finished.
One way to go could be: For every task you start, increment a private volatile int runningTasks variable. Whenever a task finishes, call another event. In the handler for that event
Decrement the variable again
When it reaches 0, activate the button again
You need to decide whether you want to be fully asynchronous or fully synchronous.
Example:
private volatile int runningTasks = 0;
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
runningTasks = 2;
...
stlFile.OnReadUpdate += stlFile_OnReadUpdate;
stlFile2.OnFinished += stlFile_OnFinished;
stlFile.OnReadUpdate += stlFile_OnReadUpdate;
stlFile2.OnFinished += stlFile_OnFinished;
List<Task> taskList = new List<Task>();
taskList.Add(...);
taskList.Add(...);
foreach (Task t in taskList)
t.Start();
}
private void stlFile_OnFinished(object sender, EventArgs e)
{
runningTasks--;
if (runningTasks <= 0)
this.Invoke(new Action(() => button1.Enabled = true));
}
I have a timer tick event that it's interval set to 10000
private void timer1_Tick(object sender, EventArgs e)
{
Update();
}
In Update i have:
public int Update()
{
counter += 1;
int position = (int)Math.Round((counter / updateTime) * 100);
xpProgressBar1.Text = counter.ToString() + " %";
xpProgressBar1.Position = counter;
if (counter == 10)
{
DownloadingHtml();
ScrollNews();
counter = 0;
}
return position;
}
Then in the backgroundworker do work:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int position = NewsUpdate();
object param = "report";
backgroundWorker1.ReportProgress(position, param);
}
And the backgroundworker progress event:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
xpProgressBar1.Text = e.ProgressPercentage.ToString() + " %";
xpProgressBar1.Position = e.ProgressPercentage;
if (counter == 10)
{
DownloadingHtml();
ScrollNews();
counter = 0;
}
}
What i want to do in the first step is that the Update method will be called each 10 seconds but through the backgroundworker.
In the second step i want to add to the backgroundworker another method:
public void ScrollNews()
{
label3.Text = SaveOldHtml.HtmlLoadedFileNumber.ToString();
richTextBox1.Clear();
combindedString = string.Join(Environment.NewLine, ListsExtractions.myList);
richTextBox1.SelectAll();
richTextBox1.SelectionAlignment = HorizontalAlignment.Right;
richTextBox1.Text = combindedString;
scrollerText = string.Join(Environment.NewLine, ListsExtractions.myListWithoutLinks);
scroller1.TextToScroll = scrollerText;
if (NewsLevels.newsLevel && NewsLevels.shouldStart)
{
scroller1.Start();
NewsLevels.shouldStart = false;
}
if (NewsLevels.newsLevel == false && NewsLevels.shouldStart)
{
scroller1.Start();
NewsLevels.shouldStart = false;
}
string[] rlines = richTextBox1.Lines;
richTextBox1.SelectionStart = 0;
richTextBox1.SelectionLength = rlines[0].Length;
richTextBox1.SelectionColor = Color.Red;
richTextBox1.Select(rlines[0].Length, rlines[1].Length + 1);
richTextBox1.SelectionColor = Color.Green;
}
The ScrollNews method is being called from the Update method and it's updating richTextBox1 and Scroller1 with text.
And in the end i want to add the last method in Update:
private void DownloadingHtml()
{
using (var webClient = new WebClient())
{
webClient.Encoding = System.Text.Encoding.GetEncoding(1255);
page = webClient.DownloadString("http://rotter.net/scoopscache.html");
}
StreamWriter w = new StreamWriter(#"d:\rotterhtml\rotterscoops.html");
w.Write(page);
w.Close();
page = #"d:\rotterhtml\rotterscoops.html";
listsext.Ext(page);
count++;
}
All this methods i want to be working from the backgroundworker.
In the form1 constructor i did that first it will call the DownloadingHtml method once then call the ScrollNews method once then activate the backgroundworker and then start the timer1.
Seems like you are misusing BackgroundWorker class. It is usually used to perform a single time-consuming action that should not block th UI. All time consuming operations should take place in OnDoWork event that is executed on a separate thread. Report progress is executed on UI thread and is used to update progress bar and other UI elements that show progress.
timer1_Tick is executed on the UI thread and blocks your UI while executing. It's not a good idea to perform any downloading or processing there if you don't want your UI to hang.
You could start TPL Task, Thread or just start BackgroundWorker anew in every timer1_Tick execution. This Task or Thread can then report progress and update current UI state, calling form's thread-safe methods. BackgroundWorker can use it's own ReportProgress mechanism for this purpose.
In case of using separate Task or Thread, each method called from a separate thread should check Form's InvokeRequired and call BeginInvoke to perform thread-safe UI update. This is described well here: beginInvoke, GUI and thread and in many other similar questions.
I have a problem with background worker.
I don't know how exactly to describe it.
Actually its a game and with the background worker ever x milisecs i update the progress bar and check if anyone has lost/won or the time is up.
If someome has win the game ends.
If both players have lost/time is up the game goes to the next round.
The ploblem occurs when both players have lost. The method NextRound in the SetTime method,
runs twice.
Here is the code:
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.SetTime(e.ProgressPercentage);
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(3000);
BackgroundWorker worker = sender as BackgroundWorker;
int tick = ProgLib.maxTime * 10;
for (int i = 1; i <= 100; i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
Thread.Sleep(tick);
worker.ReportProgress(i);
}
}
}
private void SetTime(double k)
{
this.time.Bar1.Value = k;
this.time.Bar2.Value = k;
if (k >= 100 || (Gallery1.hasLost() && Gallery2.hasLost()) || ((Gallery1.isWinner() || Gallery2.isWinner())))
{
if (bw == null)
return;
bw.CancelAsync();
bw.Dispose();
bw = null;
saveData();
ProgLib.isAnyoneWinner(Gallery1.isWinner(), Gallery2.isWinner());
if (ProgLib.gameHasended())
{
gameHasEnded();
}
else
{
next_round();
}
}
}
private void next_round()
{
Thread nextRoundThread = new Thread((Object Send) =>
{
MainThread.Send((Object send) =>
{
Gallery1.hidePanel.Visibility = Visibility.Visible;
Gallery2.hidePanel.Visibility = Visibility.Visible;
ProgLib.nextLetter();//goes to next letter
LetterToPlay1.setLetter(ProgLib.getArrabicLetter(ProgLib.getCurentLetter()));//sets the next letter
LetterToPlay2.setLetter(ProgLib.getArrabicLetter(ProgLib.getCurentLetter()));
}, null);
Thread SoundThread = new Thread((Object send) =>
{
//Here Must Delay enought so the animation stops the hear the bell and the the letter, and then the game starts
Thread.Sleep(1800);
ProgLib.playOtherSound(ProgLib.Sounds.Chimes);//Bell Sound
Thread.Sleep(100);
//ProgLib.PlayLetterSound(ProgLib.getCurentLetter());//Letter Sound
ProgLib.playOtherSound(ProgLib.Sounds.Cat_Yawn);//TestOnly
});
SoundThread.IsBackground = true;
SoundThread.Start();
Thread.Sleep(3000);
MainThread.Send((Object send) =>
{
Gallery1.refresh();//galleries refresh so that the magician hides.
Gallery2.refresh();//
Gallery1.hidePanel.Visibility = Visibility.Hidden;//hide the Big Magician of mistakes
Gallery2.hidePanel.Visibility = Visibility.Hidden;
}, null);
});
nextRoundThread.IsBackground = true;
nextRoundThread.Start();
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerAsync();
}
There's a threading race in your code. Your worker will call ReportProgress() and immediately iterate the loop. Racing past the CancellationPending property check and falling asleep again.
Your SetTime() method runs later. And calls CancelAsync() but that doesn't have any effect at all since the worker is sleeping. Not until it wakes up again, calls ReportProgress() again, iterates the loop and then sees CancellationPending set to true.
Your SetTime() method will be called again, even though you've already ended the game.
Threading is rife with problems like this. A band-aid is to check for CancellationPending after the Sleep() call. Which works 99.999% of the time. Getting to 100% requires a pretty drastic rewrite that uses proper locking.
I have an array of five threads. Each thread contains the same form, each form is put on to the screen in a different location (still working on that method :P).
I am trying to have each form load its contents (an image) before the other forms have finishing being placed. At the moment this works for the first form, but the others are blank or disappear :P
Originally each form would be placed but the method would need to finish before all the forms contents were displayed.
Any help would be appreciated, thanks :)
public partial class TrollFrm : Form
{
int number = 0;
public TrollFrm()
{
InitializeComponent();
startThreads();
}
private void TrollFrm_Load(object sender, EventArgs e)
{
}
private void TrollFrm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
}
public void startThreads()
{
Thread[] ThreadArray = new Thread[5];
for (int i = 0; i < 5; i++)
{
ThreadArray[i] = new Thread(new ThreadStart(createForm));
ThreadArray[i].Start();
}
}
public void createForm()
{
Form frm = new TrollChildFrm();
Random randomX = new Random();
Random randomY = new Random();
number++;
int xValue;
int yValue;
if (number % 2 == 0) //number is even.
{
xValue = (Convert.ToInt32(randomX.Next(1, 1920))) + 200;
yValue = (Convert.ToInt32(randomY.Next(1, 1080))) - 200;
}
else //number is not even.
{
xValue = (Convert.ToInt32(randomX.Next(1, 1920))) - 200;
yValue = (Convert.ToInt32(randomY.Next(1, 1080))) + 200;
}
frm.Show();
frm.Location = new Point(xValue, yValue);
Thread.Sleep(1000);
}
Your forms are not displaying correctly because they are not running on a thread with a message loop. The general rule is that all UI element accesses must occur on the main UI thread.
Since you have a call to Thread.Sleep(1000) I am going to assume that you want to wait 1 second between the initial display of each form. In that case I would use a System.Windows.Forms.Timer who's Tick event will call createForm directly. Enable the timer, let 5 Tick events come through, and then disable the timer. I see no need to create any threads at all.
The reason your forms aren't displaying is because you are running inside one method on the main UI thread. Instead, you could create a method that spawns a new form and launch that at certain intervals on another thread (making sure the form handling is done on the main UI thread). So you could do something like:
(Pseudo Code)
private const int TIME_THRESHOLD = 100;
int mElapsedTime = 0;
Timer mTimer = new Timer();
.ctor
{
mTimer.Elapsed += mTimer_Elapsed;
}
private void mTimer_Elapsed(...)
{
mElapsedTime++;
if (mElapsedTime >= TIME_THRESHOLD)
{
mElapsedTime = 0;
SpawnForm();
}
}
private void SpawnForm()
{
// Make sure your running on the UI thread
if (this.InvokeRequired)
{
this.BeginInvoke(new Action(SpawnForm));
return;
}
// ... spawn the form ...
}
This is just an example of what I was proposing - it would not look exactly like this in the code, but this should give you an idea of the execution steps.
I would suggest to use Thread.Sleep(1000) in this manner
Caller section
for (int i = 0; i < 5; i++)
{
ThreadArray[i] = new Thread(new ThreadStart(createForm));
ThreadArray[i].Start();
}
Thread.Sleep(1000);
Also in the method that executing the work for the thread.
while(!something)
{
Thread.Sleep(1000)
}
In a win form application, I have an array of threads which are started like this:
bool stop = false;
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.Length; i++)
threads[i] = new Thread(new ThreadStart(Job));
// How to make sure all threads have exited, when the boolean = false
void Job()
{
while (!stop)
// Do something
}
Now if user press STOP, the boolean value for stop will set to true, so threads exit the Job method one after another. How can I make sure all threads are exited?
NOTE: I need traditional threading for my case and TaskLibrary doesn't fit my scenario.
Use the Join method to check if all threads have stopped.
foreach (var t in threads)
{
t.Join();
}
Have you thought about using BackgroundWorkers instead? You said "traditional threads"..I'm not exactly sure what you mean so I don't know if this is a valid proposal or not, but here it is anyways in case Join() doesn't solve your problem
BackgroundWorker[] workers = new BackgroundWorker[10];
bool allThreadsDone = false;
// initialize BackgroundWorkers
for (int i = 0; i < 10; i++)
{
workers[i] = new BackgroundWorker();
workers[i].WorkerSupportsCancellation = true;
workers[i].RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
workers[i].DoWork += new DoWorkEventHandler(AlgorithmsUI_DoWork);
workers[i].RunWorkerAsync();
}
// thread entry point..DoWork is fired when RunWorkerAsync is called
void AlgorithmsUI_DoWork(object sender, DoWorkEventArgs e)
{
while (!stop)
// do something
}
// this event is fired when the BGW finishes execution
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bool threadsStillRunning = false;
foreach (BackgroundWorker worker in workers)
{
if (worker.IsBusy)
{
threadsStillRunning = true;
break;
}
}
if (!threadsStillRunning)
allThreadsDone = true;
}
protected override OnFormClosing(FormClosingEventArgs e)
{
if (!allThreadsDone)
{
e.Cancel = true;
MessageaBox.Show("Threads still running!");
}
}
This should prevent your form from closing if any threads are still running.
I'm not sure if this is what you're looking for, but here's a simple solution I used back in .NET 3.0 to make sure a large but deterministic number of threads had completed before continuing:
Global:
AutoResetEvent threadPoolComplete = new AutoResetEvent(false);
static int numThreadsToRun;
As you activate the threads:
numThreadsToRun = [number of threads];
[start your threads];
threadPoolComplete.WaitOne();
At the end of each thread's code:
if (Interlocked.Decrement(ref numThreadsToRun) == 0)
{
threadPoolComplete.Set();
}