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

С++ Thread, общие данные

У меня есть приложение, в котором работает 2 потока... Есть ли какой-либо certanty, что, когда я изменяю глобальную переменную из одного потока, другой заметил это изменение? У меня нет никакой синхронизации или системы взаимного исключения на месте... но должен ли этот код работать все время (представьте себе глобальный bool с именем dataUpdated):

Тема 1:

while(1) {
    if (dataUpdated)
        updateScreen();
    doSomethingElse();
}

Тема 2:

while(1) {
    if (doSomething())
        dataUpdated = TRUE;
}

Компилятор вроде gcc оптимизирует этот код таким образом, чтобы он не проверял глобальное значение, учитывая его значение во время компиляции (потому что он не изменился при одном и том же thred)?

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

4b9b3361

Ответ 1

Да. Нет. Может быть.

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

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

Неустойчивый считается вредным и Ядра памяти ядра Linux хороший фон по основным вопросам; Я не знаю ничего подобного, написанного специально для потокования. К счастью, нити не поднимают эти проблемы почти так же часто, как и аппаратные периферийные устройства, хотя описанный вами случай (флаг, указывающий завершение, с другими данными, считающимися действительными, если установлен флаг) - это именно то, matterns...

Ответ 2

Вот пример, который использует переменные состояния форматирования:

bool _updated=false;
boost::mutex _access;
boost::condition _condition;

bool updated()
{
  return _updated;
}

void thread1()
{
  boost::mutex::scoped_lock lock(_access);
  while (true)
  {
    boost::xtime xt;
    boost::xtime_get(&xt, boost::TIME_UTC);
    // note that the second parameter to timed_wait is a predicate function that is called - not the address of a variable to check
    if (_condition.timed_wait(lock, &updated, xt))
      updateScreen();
    doSomethingElse();
  }
}

void thread2()
{
  while(true)
  {
    if (doSomething())
      _updated=true;
  }
}

Ответ 3

Используйте блокировку. Всегда всегда используйте блокировку для доступа к общим данным. Маркировка переменной как volatile не позволит компилятору оптимизировать чтение памяти, но не предотвратит другие проблемы, такие как переупорядочение памяти. Без блокировки нет гарантии, что запись в памяти doSomething() будет видна в функции updateScreen().

Единственный безопасный способ - использовать забор памяти, явно или неявно, используя функцию Interlocked *, например.

Ответ 4

Используйте ключевое слово volatile, чтобы указать компилятору, что значение может измениться в любое время.

volatile int myInteger;

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

Chris Jester-Young отметил, что в многопроцессорных системах могут возникать проблемы согласованности с таким изменением переменных значений. Это соображение, и это зависит от платформы.

На самом деле, есть действительно два соображения, чтобы думать об отношении к платформе. Это когерентность и атомарность транзакций памяти.

Атоматичность на самом деле является соображением как для одноплатных, так и для многопроцессорных платформ. Проблема возникает из-за того, что переменная, вероятно, является многобайтовой по своей природе, и возникает вопрос, может ли один поток увидеть частичное обновление значения или нет. т.е.: Некоторые байты изменены, контекстный переключатель, недопустимое значение, считанное прерыванием потока. Для одной переменной, которая находится на натуральном размерном слое машины или меньше и естественно выровнена, не должно вызывать беспокойства. В частности, тип int всегда должен быть ОК в этом отношении, если он выровнен - ​​это должно быть стандартным случаем для компилятора.

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

Последний вопрос относительно атомарности, который возникает, специфичен для атомарности чтения-модификации-записи. То есть, как вы гарантируете, что если значение будет считаться обновленным по значению и написанным, это произойдет атомарно, даже для процессоров, если их больше одного. Таким образом, для этого, чтобы работать без конкретных объектов синхронизации, потребовалось бы, чтобы все потенциальные потоки, обращающиеся к переменной, были читателями ТОЛЬКО, но ожидали, что только один поток может когда-либо быть писателем за один раз. Если это не так, вам нужен объект синхронизации, доступный для обеспечения атомарных действий над действиями read-modify-write для переменной.

Ответ 5

В вашем решении будет использоваться 100% процессор, среди прочих проблем. Google для "переменной условия".

Ответ 6

Крис Джет-Янг отметил, что:

Это работает только в модели памяти Java 1.5+. Стандарт С++ не предназначен для потоковой передачи, а волатильность не гарантирует согласованность памяти между процессорами. Для этого вам нужен барьер памяти

поэтому единственный верный ответ - это внедрение системы синхронизации, верно?

Ответ 7

Используйте ключевое слово volatile, чтобы указать компилятору, что значение может измениться в любое время.

volatile int myInteger;

Ответ 8

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

Ответ 9

Если область прав ( "extern", глобальная и т.д.), это изменение будет замечено. Вопрос в том, когда? И в каком порядке?

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

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

Отъезд Опасность трубопровода в википедию или поиск в Google для "переупорядочения команд компилятора"

Ответ 10

Как говорили другие, ключевое слово volatile - ваш друг.: -)

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

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

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

При всем этом вы можете найти лучшие результаты (как указано Jeff) с помощью семафора или переменной условия.

Это является разумным введением к теме.