Моя многопоточная программа работает медленно или блокируется на двухъядерном компьютере, пожалуйста, помогите - PullRequest
2 голосов
/ 04 мая 2010

У меня есть программа с несколькими потоками, один поток изменит глобальный, когда он выйдет сам, а другой поток будет повторно опросить глобальный. Никакой защиты на глобалах. Программа отлично работает на однопроцессорных. На двухъядерной машине он работает некоторое время, а затем останавливается либо в Sleep (0), либо в SuspendThread (). Кто-нибудь сможет мне помочь в этом?

Код будет выглядеть так:

Thread 1:

do something...
while(1)
{
.....
flag_thread1_running=false;
SuspendThread(GetCurrentThread());
continue;

}

Thread 2
flag_thread1_running=true;
ResumeThread(thread1);
.....do some other work here....
while(flag_thread1_running) Sleep(0);
....

Ответы [ 4 ]

14 голосов
/ 04 мая 2010

Тот факт, что вы не видите никаких проблем на однопроцессорной машине, но видите проблемы на многопроцессорной машине, является артефактом относительно большой степени детализации переключения контекста потока на однопроцессорной машине. Поток будет выполняться в течение N промежутков времени (миллисекунд, наносекунд и т. Д.), Прежде чем планировщик потока переключит выполнение на другой поток. Многие инструкции процессора могут выполняться в типичном временном интервале потока. Вы можете думать об этом, как о довольно большом куске эксклюзивного процессорного времени «free play», в течение которого вы, вероятно, не столкнетесь с конфликтами ресурсов, потому что на процессоре больше ничего не выполняется.

Однако при работе на многопроцессорной машине инструкции ЦП в двух потоках выполняются точно в одно и то же время. Размер порции «свободной игры» близок к нулю.

Чтобы воспроизвести проблему конфликта ресурсов между двумя потоками, необходимо, чтобы поток 1 обращался к ресурсу, а поток 2 - к ресурсу одновременно или почти одновременно.

При переключении потоков с высокой степенью детализации, которое происходит на однопроцессорной машине, вероятность того, что переключение потока произойдет точно в нужном месте, невелика, поэтому при обычном использовании программы на машине с однопроцессорным доступом программа может никогда не сработать.

В многопроцессорной машине инструкции выполняются одновременно в двух потоках, поэтому шансы одновременного одновременного доступа потока 1 и потока 2 к одному и тому же ресурсу намного, намного выше - в тысячи раз больше, чем однопроцессорный сценарий.

Я видел, как это происходило много раз: приложение, которое годами работало нормально на однопроцессорных машинах, внезапно начинает сбой повсеместно при запуске на новой многопроцессорной машине. Причина заключается в скрытой ошибке многопоточности в исходном коде, которая просто никогда не соответствует правильному совпадению временного среза для воспроизведения на машинах uniproc.

При работе с многопоточным кодом абсолютно непросто тестировать код на многопроцессорном оборудовании. Если в вашем коде возникают проблемы с коллизиями потоков, они быстро появятся на многопроцессорной машине.

Как уже отмечали другие, не используйте SuspendThread (), если вы не являетесь отладчиком. Используйте мьютексы или другие объекты синхронизации для координации между потоками.

3 голосов
/ 04 мая 2010

Попробуйте использовать что-нибудь более похожее на WaitForSingleObjectEx вместо SuspendThread.

2 голосов
/ 04 мая 2010

Вы попали в состояние гонки. Поток 2 может выполнить flag_thread1_running = true; перед выполнением потока 1 flag_thread1_running = false.

Это вряд ли произойдет на одном процессоре, потому что при обычном интервале планирования 10-20 мс вы вряд ли столкнетесь с проблемой. Там тоже будет, но очень редко.

Использование правильных примитивов синхронизации обязательно. Вместо bool используйте event. Вместо проверки bool в цикле используйте WaitForSingleObject (или WaitForMultipleObjects для более сложных вещей позже).

Можно выполнить синхронизацию между потоками, используя простые переменные, но это редко хорошая идея, и это довольно сложно сделать правильно - ср. Как мне написать структуру без блокировки? . Определенно не стоит выполнять планирование с использованием Sleep, Suspend или Resume.

0 голосов
/ 04 мая 2010

Полагаю, вы уже знаете, что опрос глобального флага - это "плохая идея и торговля"; так что я пропущу эту маленькую речь. Попробуйте добавить volatile к объявлению флага. Это должно заставить каждое чтение этого читать из памяти. Без volatile реализация могла бы считывать флаг в регистр и не извлекать его из памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...