Код: Выделить всё
// Thread A:
// smart_ptr copy ctor
smart_ptr(const smart_ptr& other) {
...
control_block_ptr = other->control_block_ptr;
control_block_ptr->refs.fetch_add(1, memory_order_relaxed);
...
}
// Thread D:
// smart_ptr destructor
~smart_ptr() {
if (control_block_ptr->refs.fetch_sub(1, memory_order_acq_rel) == 1) {
delete control_block_ptr;
}
}
Один из ответов содержит отрывок:
Увеличить счетчик ссылок всегда можно с помощью Memory_order_relaxed: новые ссылки на объект можно сформировать только из существующей ссылки и передать существующую ссылку из одного потока в другой уже должен предоставить все необходимые синхронизация.
Также я нашел ссылку https://gavinchou.github.io/summary/c++ ... -ordering/, где написано :
Приращение можно ослабить (это не операция публикации, от этого приращения никто не зависит).
Эти абзацы подразумевают, что эти приращения в некотором роде независимы, и здесь https://en.cppreference.com/w/cpp/atomic/memory_order объясняется, что ослабленный порядок памяти подходит для (независимых) приращений.
Однако , в случае приведенного выше примера приращения вовсе не являются независимыми. Существуют также параллельные уменьшения, и они зависят от приращений (когда значение чтения равно 1, объект удаляется). Приращения ослаблены, поэтому декременты могут их не увидеть и удалить объект. Даже если учесть, что операции RMW всегда считывают самое последнее значение, порядок увеличения/уменьшения, видимого разными потоками, по-прежнему различен, поэтому неясно, что увидит поток, уменьшающий значение.
Есть не в этом ли проблема и не следует ли как-то синхронизировать приращения с декрементами? К сожалению, не могу понять два приведенных выше объяснения: они слишком короткие.
Подробнее здесь: https://stackoverflow.com/questions/793 ... d-with-dec