Есть форма с двумя кнопками, одна кнопка — запустить поток с ресурсоёмкой задачей, другая — остановить его. Поток для задачи берется из ThreadPool. В качестве имитации задачи используется Thread.SpinWait или просто цикл:
void WorkItem(object state)
{
int iterations = 1000000000;
while (!bStopWorkItem)
{
Thread.SpinWait(iterations);
//MySpinWait(iterations);
}
this.Invoke((Action)WorkItem_Stopped);
}
void MySpinWait(int iterations)
{
for (; iterations > 0; iterations--) { }
}
Кнопка которая останавливает поток присваивает полю bStopWorkItem значение true, и задача просто покидает его.
Проблема в том, что если в качестве имитации задачи использовать Thread.SpinWait, то через некоторое время (3-15 сек) GUI перестаёт отвечать, в то время как при MySpinWait задачу можно снять в любое время. Собсно, вопрос в том и заключается:
почему зависает GUI?
Подобное поведение характерно не только для Thread.SpinWait. У меня есть проект, где читается директория при помощи API-функций FindFirstFile, FindNextFile, и таже проблема актуальна. Причём, если чтение идет с сетевого диска или CD, то проблем нет (IMHO: в процессе чтения с медленного носителя используются задержки освобождающие кванты), но если читать с быстрого HDD, а тем более, если читать два раза подряд одну и туже директорию (при втором чтении будет использована инфа с кеша), то подвисание гарантированно.
Вот готовый проект, на примере которого видно всё, о чём я говорил:
ThreadDemo01.zip
PS
Значение 830 000 000 для переменной iterations приблизительно соответствует задержке в одну секунду на проце Athlon64 X2 5000+
После запуска с Thread.SpinWait, поводите мышью над кнопкой Stop, так GUI подвиснет быстрее.
Закоментируйте строчку с Thread.SpinWait(iterations) и разкоментируйте с MySpinWait(iterations), вы убедитесь, что при такой же загрузке проца GUI стабильно работоспособен.
Втророй пример, более навороченный, но делает приблизительно тоже:
ThreadDemo02.zip
Из второго примера видно, что если при подвешенном GUI, задачу всё же удаётся остановить, то и GUI начинает функционировать, как ни в чём не бывало, нормально (за остановку по таймауту отвечает поле "Emergency counter", где одна единица соответствует примерно одной секунде).