Добавление необходимых барьеров для такой полностью поточно-ориентированной реализации shared_ptr может повлиять на производительность. Рассмотрим следующую расу (примечание: псевдокод изобилует):
Тема 1:
global_ptr = A;
Тема 2:
global_ptr = B;
Тема 3:
local_ptr = global_ptr;
Если разбить это на составляющие операции:
Тема 1:
A.refcnt++;
tmp_ptr = exchange(global_ptr, A);
if (!--tmp_ptr.refcnt) delete tmp_ptr;
Тема 2:
B.refcnt++;
tmp_ptr = exchange(global_ptr, B);
if (!--tmp_ptr.refcnt) delete tmp_ptr;
Тема 3:
local_ptr = global_ptr;
local_ptr.refcnt++;
Ясно, что если поток 3 читает указатель после замены A, то B переходит и удаляет его до того, как счетчик ссылок может быть увеличен, произойдут плохие вещи.
Чтобы справиться с этим, нам нужно использовать фиктивное значение, пока поток 3 выполняет обновление refcnt:
(примечание: сравнение_exchange (переменная, ожидаемое, новое) атомарно заменяет значение в переменной новым, если оно в настоящее время равно новому, и возвращает истину, если это было успешно)
Тема 1:
A.refcnt++;
tmp_ptr = global_ptr;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A))
tmp_ptr = global_ptr;
if (!--tmp_ptr.refcnt) delete tmp_ptr;
Тема 2:
B.refcnt++;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A))
tmp_ptr = global_ptr;
if (!--tmp_ptr.refcnt) delete tmp_ptr;
Тема 3:
tmp_ptr = global_ptr;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, BAD_PTR))
tmp_ptr = global_ptr;
local_ptr = tmp_ptr;
local_ptr.refcnt++;
global_ptr = tmp_ptr;
Теперь вам нужно было добавить цикл с атомами в середине вашей / read / операции. Это не очень хорошая вещь - это может быть очень дорого на некоторых процессорах. Более того, ты тоже занят - ждешь. Вы можете начать умничать с фьютексами и еще чем-то - но к этому моменту вы заново изобрели замок.
Эта стоимость, которую должны нести каждая операция, и по своему характеру очень похожа на то, что блокировка даст вам в любом случае, поэтому вы обычно не видите таких поточно-ориентированных реализаций shared_ptr. Если вам нужна такая вещь, я бы рекомендовал обернуть мьютекс и shared_ptr в удобный класс для автоматизации блокировки.