Есть форма с двумя кнопками, одна кнопка — запустить поток с ресурсоёмкой задачей, другая — остановить его. Поток для задачи берется из 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", где одна единица соответствует примерно одной секунде).
S>После запуска с Thread.SpinWait, поводите мышью над кнопкой Stop, так GUI подвиснет быстрее.
S>Закоментируйте строчку с Thread.SpinWait(iterations) и разкоментируйте с MySpinWait(iterations), вы убедитесь, что при такой же загрузке проца GUI стабильно работоспособен.
Что бы легче было понять о чем идет речь, можете скачать видео с gmail.com (см. в черновиках)
логин: share.sasha
пароль: entershare
PS
Thread.SpinWait() — это не организация задержек, это имитация работы (...метод SpinWait, который не отказывается от времени CPU, а наоборот, загружает процессор ...)
Windows.Forms.Timer — используется для секундомера вверху формы, а так же позволяет видеть, когда секундомер останавливается, значит GUI подвис
У моего знакомого лэптоп на Centrino и он говорит что никакого подвисания у него нет. Я проверял на Athlon64 X2 5000+, PentiumIII 1000 — проблема есть.
Здравствуйте, Shadedsun, Вы писали:
S>>После запуска с Thread.SpinWait, поводите мышью над кнопкой Stop, так GUI подвиснет быстрее.
S>>Закоментируйте строчку с Thread.SpinWait(iterations) и разкоментируйте с MySpinWait(iterations), вы убедитесь, что при такой же загрузке проца GUI стабильно работоспособен.
S>Что бы легче было понять о чем идет речь, можете скачать видео с gmail.com (см. в черновиках)
S>логин: share.sasha
S>пароль: entershare
S>PS
S>
S>Thread.SpinWait() — это не организация задержек, это имитация работы (...метод SpinWait, который не отказывается от времени CPU, а наоборот, загружает процессор ...)
S>Windows.Forms.Timer — используется для секундомера вверху формы, а так же позволяет видеть, когда секундомер останавливается, значит GUI подвис
S>У моего знакомого лэптоп на Centrino и он говорит что никакого подвисания у него нет. Я проверял на Athlon64 X2 5000+, PentiumIII 1000 — проблема есть.
S>
А hyper threading есть?
Здравствуйте, Shadedsun, Вы писали:
S>Есть форма с двумя кнопками, одна кнопка — запустить поток с ресурсоёмкой задачей, другая — остановить его. Поток для задачи берется из ThreadPool. В качестве имитации задачи используется Thread.SpinWait или просто цикл:
S>S>void WorkItem(object state)
S>{
S> int iterations = 1000000000;
S> while (!bStopWorkItem)
S> {
S> Thread.SpinWait(iterations);
S> //MySpinWait(iterations);
S> }
S> this.Invoke((Action)WorkItem_Stopped);
S>}
S>void MySpinWait(int iterations)
S>{
S> for (; iterations > 0; iterations--) { }
S>}
S>
S>Кнопка которая останавливает поток присваивает полю bStopWorkItem значение true, и задача просто покидает его.
S>Проблема в том, что если в качестве имитации задачи использовать Thread.SpinWait, то через некоторое время (3-15 сек) GUI перестаёт отвечать, в то время как при MySpinWait задачу можно снять в любое время. Собсно, вопрос в том и заключается: почему зависает GUI?
S>Подобное поведение характерно не только для Thread.SpinWait. У меня есть проект, где читается директория при помощи API-функций FindFirstFile, FindNextFile, и таже проблема актуальна. Причём, если чтение идет с сетевого диска или CD, то проблем нет (IMHO: в процессе чтения с медленного носителя используются задержки освобождающие кванты), но если читать с быстрого HDD, а тем более, если читать два раза подряд одну и туже директорию (при втором чтении будет использована инфа с кеша), то подвисание гарантированно.
S>Вот готовый проект, на примере которого видно всё, о чём я говорил:ThreadDemo01.zip
Не совсем уверен, что MySpinWait в Вашем примере выполняет какую-то работу. Об агрессивной оптимизации слышали? Я до недавнего времени тоже ничего не знал. Но взгляните сюда:
http://forum.vingrad.ru/index.php?showtopic=180722&view=findpost&p=1310994. Поэтому и подвисаний нет. Я так думаю.
Здравствуйте, Experimenter, Вы писали:
E>Не совсем уверен, что MySpinWait в Вашем примере выполняет какую-то работу. Об агрессивной оптимизации слышали? Я до недавнего времени тоже ничего не знал. Но взгляните сюда: http://forum.vingrad.ru/index.php?showtopic=180722&view=findpost&p=1310994. Поэтому и подвисаний нет. Я так думаю.
for (int i = 0; i < 100; i++)
{
int sum = i + i;
}
Тут никаких сообщений не будет, но тело цикла будет пустым в сборке. Сам цикл оставляют видимо из-за того, что есть такая штука как busy-waiting loops. Хотя, честно говоря, я не очень понимаю зачем это делается — может просто страшно удалить полностью.
Угадайте, откуда это.
Ошибка в URL:
ThreadDemo02.zip
Хоть бы ктонить подсказал
Здравствуйте, Shadedsun, Вы писали:
S>Здравствуйте, Experimenter, Вы писали:
E>>Не совсем уверен, что MySpinWait в Вашем примере выполняет какую-то работу. Об агрессивной оптимизации слышали? Я до недавнего времени тоже ничего не знал. Но взгляните сюда: http://forum.vingrad.ru/index.php?showtopic=180722&view=findpost&p=1310994. Поэтому и подвисаний нет. Я так думаю.
S>S>
S>for (int i = 0; i < 100; i++)
S>{
S> int sum = i + i;
S>}
S>
S>Тут никаких сообщений не будет, но тело цикла будет пустым в сборке. Сам цикл оставляют видимо из-за того, что есть такая штука как busy-waiting loops. Хотя, честно говоря, я не очень понимаю зачем это делается — может просто страшно удалить полностью.
S>Угадайте, откуда это.
В том же топике, немного выше:
S>Про статью:
S>
S>1. Перечисления. Автор двоечник и мерил скорость выполнения пустого цикла, компилер выкидывает такое тело, в этом может убедиться каждый, S>воспользовавшись рефлектором. Я на месте компилера ещё бы и сам цикл выкидывал. Даже если автор отключил оптимизацию, то джитер всё равно тело S>цикла выкинет:
S>
http://www.interface.ru/home.asp?artId=7747 — это ссылка на статью, которая обсуждалась.
Пустые циклы не рулят.