The following code is a simplified proof of concept. In a windows forms application there is a FormClosing event handler to perform some cleanup. The application launches some threads which write on the Form using Control.Invoke. I know I could use Control.BeginInvoke but I would like to understand better what happens and find another solution.
List<Thread> activeThreadlist;
volatile bool goOnPolling = true;
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
goOnPolling = false;
Thread.Sleep(1000);
foreach (Thread element in activeThreadlist)
if (element.IsAlive)
element.Abort();
}
A flag is set to false in order to stop the loop on the threads, they have time to terminate and if one is still alive it is terminated with abort.
Here is the code executed by the other threads:
while (goOnPolling)
{
//some long elaboration here
if (!goOnPolling) continue;
aControl.Invoke(new Action(() =>
{
//display some result about the elaboration
}));
if (!goOnPolling) continue;
//some long elaboration here
}
In about 50% of the cases when the form is closed the threads blocks on Control.Invoke so they don't terminate during the sleep time and Abort is called. I thought checking the flag goOnPolling before calling Invoke would have been sufficient in 99.999% of the cases, but I was wrong. As stated in the code the threads do long elaborations (at least 100ms) so I expected goOnPolling was likely to change during them. Why am I wrong ? Is there another easy solution without recurring to BeginInvoke which creates an additional thread ?
Update
After reading the comments I realized the title was wrong. I initially wrote deadlock but it is only an unexpected (for me) block condition. In the beginning I couldn't understand why my threads were still alive after having waited their terminations for 1000ms, so I put an Abort to find out. I completely misunderstood the difference between Invoke and BeginInvoke, thanks for clarifying it.
@Jon B
I agree break is better suited than continue but that code is inside while (goOnPolling) block, so I could save only some cpu cycles, nothing more.
Any idea why so often goOnPolling changes during the early stages of Invoke ? If I perform a long elaboration it should happen there most of the time.