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

Является ли указатель разыменованием атома?

Позволяет сказать, что у меня есть указатель на целое число.

volatile int* commonPointer = new int();

И у меня есть несколько потоков, которые разыгрывают этот указатель.

int blah = *commonPointer;

Но один поток должен изменить этот адрес указателя:

int* temp = new int();
int* old = commonPointer;
InterlockedExchange(&commonPointer,temp);
delete old;

Теперь давайте проигнорируем тот факт, что некоторые потоки могут читать "старое" значение, а некоторые могут читать "новое" значение, это не проблема в моем случае.

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

4b9b3361

Ответ 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 выше]