Пока цикл не завершится без "usleep ()" или "printf" - PullRequest
0 голосов
/ 31 октября 2018

Мне любопытно узнать, знает ли кто-нибудь, почему простой цикл while в c ++ не завершится без «usleep» или «printf»?

У меня есть логическое значение, внешнее значение которого изменилось, и его значение предназначено для завершения цикла while. Я также попробовал это, и это терпит неудачу:

if (!run) { break; }

Отлично работает с одним из "usleep" или "printf" в цикле.

У меня есть чувство, что это как-то связано с прерыванием, но я не уверен, почему.

while (run)
{
     // Do something

     // usleep OR printf
}

Хотя я легко могу выполнить «usleep (0)», и это работает, мне довольно любопытно, почему это происходит. Моя система - Ubuntu 16.04, работает на C ++ 11 (GCC / G ++ 5.4).

Спасибо, CaptainJL

Ответы [ 2 ]

0 голосов
/ 31 октября 2018

Официальный ответ: если вы разделяете (не атомарную) логическую переменную между потоками и не сериализуете доступ к этой переменной (например, с мьютексом), то вы вызвали неопределенное поведение, и программа бесплатна делать все, что угодно (работать как положено, работать как-то иначе, сбой, кража денег из кошелька и т. д.).

Более практичный ответ: на современной многоядерной машине каждое ядро ​​имеет свои собственные регистры и отдельный кэш L1, и поэтому, если поток, работающий на ядре 1, устанавливает конкретную область памяти, тогда другой поток, работающий на ядре 2 может не «увидеть» это изменение, если компилятор не предпринял определенных шагов, чтобы убедиться, что изменение распространяется по ядрам. Кроме того, если вы (программист) не приняли явных мер, чтобы компилятор знал, что определенная переменная будет использоваться несколькими потоками, оптимизатор компилятора может предположить, что эту переменную нельзя изменить вне данный поток выполнения потока, и, следовательно, может полностью удалить ваш тест состояния переменной (потому что, в конце концов, если компилятор «доказал», что значение переменной не может быть изменено, зачем тратить циклы ЦП на проверку ее состояния?).

В вашем случае, скорее всего, происходит то, что вызовы printf() или usleep() имеют побочные эффекты (например, переключатель usermode-> kernel-> usermode), которые включают в себя очистку кеша ядра, так что второй поток (при по крайней мере, в конечном итоге) «видит» изменения, сделанные первым потоком - хотя без этих вызовов нет причин синхронизировать кэши, и, следовательно, обновление никогда не «просматривается».

Практический совет: если вы собираетесь совместно использовать переменную в потоках, обязательно либо используйте std::atomic<bool> вместо простого старого bool, либо сериализуйте / защитите все обращения (читает и пишет) в эту переменную внутри мьютекса или критической секции. В противном случае вы, скорее всего, столкнетесь с таким «интересным» поведением такого рода.

0 голосов
/ 31 октября 2018

Первое предположение: переменная "run" не объявлена ​​как volatile.

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