Является ли указатель разыменованием атома?
Позволяет сказать, что у меня есть указатель на целое число.
volatile int* commonPointer = new int();
И у меня есть несколько потоков, которые разыгрывают этот указатель.
int blah = *commonPointer;
Но один поток должен изменить этот адрес указателя:
int* temp = new int();
int* old = commonPointer;
InterlockedExchange(&commonPointer,temp);
delete old;
Теперь давайте проигнорируем тот факт, что некоторые потоки могут читать "старое" значение, а некоторые могут читать "новое" значение, это не проблема в моем случае.
Может ли быть сценарий, когда один поток начинает разыменовывать указатель, точно так же, как адрес удаляется, а затем получает исключение?
Или это разыгрывающий атом достаточно, чтобы этого не произошло?
Ответы
Ответ 1
Ничто в стандарте С++ не гарантирует атомарность в этом случае.
Вы должны защитить соответствующие области кода с помощью мьютекса: даже std::atomic
недостаточно, поскольку он обеспечит только атомарный доступ к указателю, но это не будет включать операцию разыменования.
Ответ 2
Возможно, С++ 11
atomic<shared_ptr<int> >
соответствует вашим потребностям. Это предотвращает исчезновение старого значения, пока не будет указана хотя бы одна ссылка на значение.
atomic<shared_ptr<int> > commonPointer;
// producer:
{
shared_ptr<int> temp(new int);
shared_ptr<int> old= atomic_exchange(&commonPointer, temp);
//...
};// destructor of "old" decrements reference counter for the old value
// reader:
{
shared_ptr<int> current= atomic_load(&commonPointer);
// the referent pointed by current will not be deleted
// until current is alive (not destructed);
}
Однако, свободная реализация атомарного общего ptr достаточно сложна, поэтому
вероятно, блокировки или блокировки спина будут использоваться внутри реализации библиотеки (даже если реализация доступна на вашей платформе).
Ответ 3
Во-первых, volatile
в вашем объявлении не имеет реального
эффект. Во-вторых, как только вы измените значение в одном
поток и доступ к нему более чем в одном потоке, все обращения
должны быть защищены. В противном случае у вас есть поведение undefined. Я не знаю, какие гарантии
InterlockedExchange
дает, но я уверен, что это не влияет
на любой из потоков, которые его не называют.
Ответ 4
Edit2: Извините, нет, это не поможет. Вам нужны мьютексы вокруг доступа - возможно (очень вероятно), что код, сгенерированный компилятором, загружает указатель в регистр [или другое хранилище, например стек, если это процессор без регистров], затем обращается к памяти указательными точками at и в то же время указатель обновляется другим потоком. Единственный способ гарантировать правильность указателя - использовать мьютекс или подобные конструкции, чтобы закрыть весь блок доступа. Все, что еще можно провалить.
Как говорит сям, стандарт не гарантирует, что даже чтение 32-битного значения, которое указывает указатель hte, является атомарным - оно зависит от реализации системы. Однако, если вы спрашиваете: "Я получу одно значение, которое является либо старым, либо новым значением", тогда как минимум x86 и x86-64 это гарантируют. Другие архитектуры компьютеров не могут (32-разрядная реализация на процессоре SMP 68000 не гарантировала бы ее, так как записи будут 16-битными за раз, а второй процессор может написать половину из них, но не другую - не то, что Я знаю систему SMP с 68000 процессорами, когда-либо создаваемыми).
InterlockedExchange
(который не является "стандартной" функцией) гарантирует, что этот процессор потока имеет ЭКСКЛЮЗИВНЫЙ доступ к самому указателю, поэтому будет безопасным - никакой другой процессор не сможет получить доступ к указателю на этом точка. В этом весь смысл "заблокированных" инструкций в архитектуре x86 - они "безопасны" (и довольно медленны, но предполагается, что вы не делаете этого каждый раз...).
Изменить: обратите внимание, что вы должны быть осторожны с самим commonPointer
, потому что компилятор может не понимать, что вы используете другой поток для его обновления. Таким образом, вы все еще можете читать значение OLD-указателя.
Вызов функции [, которая не привязана к ничто], или объявление указателя volatile int * volatile commonPointer;
должно делать трюк. [cue people downvoting мой ответ для использования volatile
, так как "нет проблемы, к которой решение volatile
, как кто-то опубликовал ранее].
[См. edit2 выше]