Подтвердить что ты не робот

В то время как цикл с пустым телом проверяет изменчивые ints - что это значит?

Я рассматриваю класс С++, который имеет следующие строки:

while( x > y );
return x - y;

x и y являются переменными-членами типа volatile int. Я не понимаю эту конструкцию.

Я нашел здесь заглушку кода: https://gist.github.com/r-lyeh/cc50bbed16759a99a226. Я предполагаю, что это не гарантировано, будет правильным или даже работать.

4b9b3361

Ответ 1

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

В этом случае ваш код останется в цикле

 while(x>y);

и вернет значение x-y после изменения значений извне, чтобы x <= y. Точную причину, почему это написано, можно угадать, когда вы расскажете нам больше о своем коде и о том, где вы его видели. Цикл while в этом случае является методом ожидания для другого события.

Ответ 2

Кажется,

while( x > y );

представляет собой цикл . Он не остановится до x <= y. Поскольку x и y являются volatile, они могут быть изменены за пределами этой процедуры. Итак, как только x <= y станет истинным, возвращается x - y. Этот метод используется для ожидания какого-либо события.

Обновление

В соответствии с gist, который вы добавили, кажется, идея заключалась в том, чтобы реализовать поточно-безопасный кольцевой буфер без блокировки. Да, реализация неверна. Например, исходный фрагмент кода

unsigned count() const {
    while( tail > head );
    return head - tail;
}

Даже если tail становится меньше или равно head, не гарантируется, что head - tail возвращает положительное число. Планировщик может переключать выполнение на другой поток сразу после цикла while, и этот поток может изменить значение head. Во всяком случае, есть много других проблем, связанных с тем, как чтение и запись в работу с общей памятью (переупорядочение памяти и т.д.), Поэтому просто игнорируйте этот код.

Ответ 3

В других ответах уже подробно указано, что делает инструкция, но просто для того, чтобы напомнить, что, поскольку y (или head в связанном примере) объявляется как volatile изменения, внесенные в эту переменную из другого thread приведет к завершению цикла while после выполнения условия.

Однако, хотя пример связанного кодa > очень короткий, это почти идеальный пример того, как НЕ писать код.

Прежде всего, строка

while( tail > head );

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

Код становится еще лучше, когда мы идем.

buffer[head++ % N] = item;

Спасибо JAB за указание, что я ошибся с post-pre-increment здесь. Исправлены последствия. Поскольку нет lock или mutex es, мы, очевидно, должны будем принять худшее. Поток переключится после назначения значения в item и до выполнения head++. Затем Мерфи снова вызовет функцию, содержащую это утверждение, присвоив значение item в той же позиции head. После этого head увеличивается. Теперь мы снова возвращаемся к первому потоку и увеличиваем head. Поэтому вместо

buffer[original_value_of_head+1] = item_from_thread1; 
buffer[original_value_of_head+2] = item_from_thread2;

мы получим

buffer[original_value_of_head+1] = item_from_thread2; 
buffer[original_value_of_head+2] = whatever_was_there_previously;

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

И хорошо, только ради полноты, линия

while( tail > head );

в методе pop_back() должно быть

while( tail >= head );

если вы не хотите, чтобы вы могли добавить еще один элемент, чем вы на самом деле нажали (или даже поместить один элемент, прежде чем нажимать что-нибудь).

Извините за то, что в основном сводится к длинному разглагольству, но если это держит только одного человека от копирования и вставки этого непристойного кода, это стоит того.

Обновление: Думаю, я мог бы также привести пример, где код типа while(x>y); действительно имеет смысл.  На самом деле вы часто видели такой код довольно часто в "добрые старые" дни. кашель ДОС. Однако не использовался в контексте потоков. В основном, в качестве резервной копии в случае, когда регистрация прерывания была невозможна (вы, дети, могли бы перевести это как "невозможно зарегистрировать обработчик событий" ).

startAsynchronousDeviceOperation(..);

Это может быть почти что угодно, например. сообщите жесткому диску о чтении данных через DMA или сообщите звуковой карте для записи через DMA, возможно, даже вызовите функции на другом процессоре (например, в графическом процессоре). Обычно инициируется с помощью outb(2).

while(byteswritten==0); // or while (status!=DONE);

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

Ответ 4

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

Представьте себе следующее:

int i = 2;
while (i-- > 0) printf("%d", i);

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

Например,

volatile int i = 2;
this_function_runs_on_another_process_and_modifies_the_value(&i);
while(i-- > 0) printf("%d", i);