Пустой цикл while не проверяет состояние - PullRequest
5 голосов
/ 17 августа 2010

В многопоточной программе на C ++ у меня есть эквивалент этого запуска в одном потоке:

while(obj->member) { } // waiting for obj->member to be set to false in another thread

и в другом потоке для obj-> member установлено значение false. Однако, даже когда он установлен в false, цикл не прерывается. Если я изменю это на это:

while(obj->member) { Sleep(1) }

Работает как положено и прерывается, когда для obj-> member установлено значение false в другом потоке.
Почему это так работает?

Ответы [ 5 ]

10 голосов
/ 17 августа 2010

Попробуйте сделать член volatile. Это заставит его извлекаться из памяти при каждом его использовании, а не из регистра ЦП (именно так компилятор может его оптимизировать).

6 голосов
/ 17 августа 2010

Obj должно быть объявлено volatile.Это говорит компилятору, что его значение может измениться каким-то другим потоком.

Когда вы добавляете Sleep, компилятор знает, что другие потоки работают, и предполагает, что он может измениться.

4 голосов
/ 17 августа 2010

То, что это работает, в основном случайно. Если вы хотите делать подобные вещи надежно, вам просто нужно использовать какой-то механизм IPC, предоставляемый ОС, возможно, с Boost Interprocess (например, мьютекс или семафор), чтобы сделать более переносимым интерфейс.

volatile, хотя его часто выдвигают в качестве решения подобных проблем, не является ни необходимым, ни достаточным. Это может сильно повлиять на производительность, но все же недостаточно для правильной работы потоков. Если вы программируете для .NET, Microsoft определила свою версию volatile, чтобы обеспечить (хотя бы некоторую степень) безопасность потоков. В противном случае (в реальном C или C ++) он малопригоден и может причинить значительный вред.

Я должен также упомянуть, что это - длинный способ с момента первой ошибки. Еще тогда, когда не менее авторитетный, чем Андре Александреску, написал в «Докторе Доббс» довольно содержательную статью об использовании volatile для многопоточности. С тех пор он осознал, что был неправ, но (в значительной степени) ущерб был нанесен, тем более что volatile настолько близок к тому, чтобы делать правильные вещи, что очень легко ошибочно принять его за правильность / полезность в потоке .

2 голосов
/ 17 августа 2010

Лучший способ сделать это - использовать событие вместо bool.С событием вы можете ждать его без использования процессора, и вам не придется спать или использовать volitile.

0 голосов
/ 17 августа 2010

Возможно, ваша потоковая библиотека создает копию объекта при создании потока, и этот член на самом деле не разделяется между потоками, если он не объявлен как статический.

...