Еще один вопрос по поводу безопасного подсчета ссылок - PullRequest
5 голосов
/ 08 февраля 2011

Существует множество вопросов о том, как реализовать поточно-ориентированные счетчики ссылок. И наиболее часто встречающийся ответ: «использовать атомные приращения / убывания». Хорошо, это хороший способ чтения и записи refCounter без изменения потока между ними. Но.

Мой код:

void String::Release()
{
    if ( 0 == AtomicDecrement( &refCounter ) ) )
        delete buffer;
}

Итак. Я уменьшаю и читаю refCounter в безопасном месте. Но что, если другой поток увеличит значение моего refCounter, пока я сравниваю его с нулем?

Я не прав?

РЕДАКТИРОВАТЬ: (пример)

String* globalString = new String(); // refCount == 1 after that.

// thread 0:
delete globalString; 
  // This invokes String::Release().
  // After AtomicDecrement() counter becomes zero. 
  // Exactly after atomic decrement current thread switches to thread 1.

// thread 1:
String myCopy = *globalString;
  // This invokes AddRef(); 
  // globalString is alive;
  // internal buffer is still not deleted but refCounter is zero;
  // We increment and switch back to thread 0 where buffer will be 
  // succefully deleted;

Я не прав?

Ответы [ 2 ]

2 голосов
/ 08 февраля 2011

Будь осторожен!

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

Я видел код, похожий на тот, что в твоем вопросе, который заканчивается довольно плохо ...

В вашем случае не только то, что кто-то может увеличить счетчик после вашего сравнения, но и какой-то поток может получить счетчик со значением 1, затем вы уменьшите и УДАЛИТЕ буфер, а другой поток использует удаленную память ... CRASH

my2c

1 голос
/ 08 февраля 2011

Ваш пример звучит прямо для меня.

Однако проблема здесь не в атомарных операциях, а в ручном удалении объекта и последующей ссылке на объект, который скоро будет удален. Что если счетчик ссылок вместо 1 равен 8?

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

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

...