Использование логической переменной volatile для ожидания ожидания

Вопрос возникает после прочтения некоторых кодов, написанных другими разработчиками, поэтому я провел небольшое исследование и нашел статью Андрея Александреску. В его статья он говорит, что можно использовать переменную логическую переменную для занятого ожидания (см. первый пример с Wait / Wakeup)

class Gadget
{
public:
void Wait()
{
while (!flag_)
{
Sleep(1000); // sleeps for 1000 milliseconds
}
}
void Wakeup()
{
flag_ = true;
}
...
private:
bool flag_;
};

Я действительно не понимаю, как это работает.

  1. volatile не гарантирует, что операции будут атомарными. Практически чтение / запись в логические переменные являются атомарными, но теория не гарантирует этого. С моей точки зрения, вышеприведенный код можно было бы безопасно переписать с помощью C ++ 11, используя функции std :: atomic :: load / store с ограничениями упорядочения в памяти получения / выпуска памяти соответственно.
  2. У нас нет этой проблемы в описанном примере, но если у нас более одной записи, у нас могут быть проблемы с упорядочением памяти. Volatile не является забором, она не требует упорядочения памяти, она просто предотвращает оптимизацию компилятора.

Так почему же так много людей используют изменчивый bool для занятого ожидания и действительно ли он переносим?

1

Решение

В статье не говорится, что volatile это все, что вам нужно (на самом деле, это не так), только то, что это может быть полезно.

Если вы сделаете это, и если вы используете простой общий компонент LockingPtr, Вы можете писать поточно-ориентированный код и гораздо меньше беспокоиться о состоянии гонки, потому что компилятор будет беспокоиться за вас и будет старательно указывать места, где вы ошибаетесь.

2

Другие решения

Я действительно не понимаю, как это работает.

Он опирается на два предположения:

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

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

приведенный выше код можно безопасно переписать с помощью C ++ 11, используя std::atomic

Сегодня это может и должно быть. В 2001 году, когда была написана статья, не так уж много.

если у нас более одной записи, у нас могут быть проблемы с упорядочением памяти

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

почему так много людей используют изменчивый bool для занятого ожидания

Потому что они не могут или не будут менять привычки, которые они сформировали до того, как C ++ приобрел модель многопоточной памяти.

и это действительно портативный?

Нет. Модель памяти C ++ 11 не гарантирует ни одно из этих предположений, и есть большая вероятность того, что они станут непрактичными для поддержки будущего оборудования по мере роста типичного числа ядер. volatile никогда не был решением для синхронизации потоков, и теперь это вдвойне важно, поскольку язык действительно обеспечивает правильные решения.

0