Следующий код является упрощенным доказательством концепции. В приложении Windows Forms есть обработчик событий FormClosing для выполнения некоторой очистки. Приложение запускает несколько потоков, которые пишут в форму с помощью Control.Invoke. Я знаю, что мог бы использовать Control.BeginInvoke, но я хотел бы лучше понять, что происходит, и найти другое решение.
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();
}
Флаг устанавливается в false, чтобы остановить цикл в потоках, у них есть время для завершения, и если один из них все еще жив, он завершается с прерыванием.
Вот код, выполняемый другими потоками:
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
}
Примерно в 50% случаев, когда форма закрывается, потоки блокируются в Control.Invoke, поэтому они не завершаются во время сна и вызывается Abort. Я думал, что проверки флага goOnPolling перед вызовом Invoke будет достаточно в 99,999% случаев, но я ошибался. Как указано в коде, потоки выполняют длительные обработки (не менее 100 мс), поэтому я ожидал, что goOnPolling может измениться во время них. Почему я ошибаюсь? Есть ли другое простое решение без повторения BeginInvoke, которое создает дополнительный поток?
Обновлять
Прочитав комментарии, я понял, что название было неправильным. Сначала я написал deadlock, но это всего лишь неожиданное (для меня) состояние блокировки. Вначале я не мог понять, почему мои потоки все еще живы после ожидания их завершения в течение 1000 мс, поэтому я поставил Abort, чтобы выяснить это. Я совершенно неправильно понял разницу между Invoke и BeginInvoke, спасибо за пояснение.
@Джон Б
Я согласен, что break подходит лучше, чем continue, но этот код находится внутри блока while (goOnPolling), поэтому я мог сэкономить только несколько циклов процессора, ничего более.
Есть идеи, почему так часто меняется goOnPolling на ранних стадиях Invoke? Если я выполняю длинную разработку, это должно происходить там большую часть времени.