Скорее всего, это происходит из-за того, что ваш компилятор не знает, что quit_thread
может быть изменен другим потоком (потому что C не знает о потоках, по крайней мере, на момент, когда был задан этот вопрос). Из-за этого он оптимизирует цикл while
в бесконечный цикл.
Другими словами, он смотрит на этот код:
quit_thread = TRUE;
while(quit_thread);
и думает про себя: «Ха, ничто в этом цикле никогда не может изменить quit_thread
на ЛОЖЬ, поэтому кодер, очевидно, просто хотел написать while (TRUE);
».
Когда вы добавляете вызов к usleep
, компилятор думает об этом и предполагает, что вызов функции может изменить глобальный объект, поэтому он воспроизводит его безопасно и не оптимизирует.
Обычно вы помечаете переменную как volatile
, чтобы компилятор не оптимизировал ее, но в этом случае вы должны использовать средства, предоставляемые pthreads, и присоединиться к потоку после установки флага в значение true (и не переустановите подпоток, сделайте это в главном потоке после объединения, если это необходимо). Причина этого заключается в том, что объединение, вероятно, будет более эффективным, чем непрерывный цикл, ожидающий изменения переменной, поскольку поток, выполняющий объединение, скорее всего, не будет выполнен до тех пор, пока соединение не должно быть выполнено.
В вашем прядильном решении присоединяющийся поток, скорее всего, продолжит работу и поглотит ворчание процессора.
Другими словами, сделайте что-то вроде:
Main thread Child thread
------------------- -------------------
fStop = false
start Child Initialise
Do some other stuff while not fStop:
fStop = true Do what you have to do
Finish up and exit
join to Child
Do yet more stuff
И, кроме того, вы должны технически защищать общие переменные с помощью мьютексов, но это один из немногих случаев, когда все в порядке, односторонняя связь, при которой полуизмененные значения переменной не имеют значения (ложь / не- ложь).
Причина, по которой вы обычно защищаете переменную с помощью мьютекса, заключается в том, что один поток не может видеть его в полуизмененном состоянии. Допустим, у вас есть двухбайтовое целое число для подсчета некоторых объектов, и оно установлено на 0x00ff
(255).
Допустим, что поток A пытается увеличить этот счетчик, но это не атомарная операция. Он меняет верхний байт на 0x01
, но, прежде чем он получит возможность изменить нижний байт на 0x00
, поток B подключается и читает его как 0x01ff
.
Теперь это не очень хорошо, если поток B захочет что-то сделать с последним элементом, подсчитанным по этому значению. Он должен смотреть на 0x0100
, но вместо этого попытаться посмотреть на 0x01ff
, эффект которого будет неправильным, если не катастрофическим.
Если бы переменная count была защищена мьютексом, поток B не смотрел бы на нее до тех пор, пока поток A не завершил ее обновление, поэтому никаких проблем не возникло бы.
Причина, по которой однонаправленные логические значения не имеют значения, заключается в том, что любое половинное состояние также будет считаться истинным или ложным, поэтому, если поток А находился посередине между превращением 0x0000
в 0x0001
(только старший байт) поток B все равно будет видеть это как 0x0000
(false) и продолжать (пока поток A не завершит свое обновление в следующий раз).
И если поток A превращает логическое значение в 0xffff
, то половина состояния 0xff00
все равно будет считаться истинной для потока B, поэтому он выполнит свою работу до того, как поток A завершит обновление логического значения.
Ни одна из этих двух возможностей не является плохой просто потому, что в обоих случаях поток A находится в процессе изменения логического значения и в конечном итоге завершит работу. На самом деле, не имеет значения, обнаружит ли поток B чуть раньше или чуть позже.