Как реализовать потокобезопасный подсчет ссылок в C ++ - PullRequest
19 голосов
/ 18 сентября 2008

Как реализовать эффективную и поточно-ориентированную систему подсчета ссылок на процессорах X86 на языке программирования C ++?

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

Следующая статья охватывает эту тему, но требует специальных инструкций процессора:

http://www.ddj.com/architect/184401888

Ответы [ 7 ]

11 голосов
/ 18 сентября 2008

В настоящее время вы можете использовать интеллектуальный указатель Boost / TR1 shared_ptr <> для сохранения ссылок, подсчитанных ссылками.

Прекрасно работает; нет суеты, нет суеты. Класс shared_ptr <> заботится обо всей блокировке, необходимой для refcount.

4 голосов
/ 18 сентября 2008

В VC ++ вы можете использовать _InterlockedCompareExchange .

do
   read the count
   perform mathematical operation
   interlockedcompareexchange( destination, updated count, old count)
until the interlockedcompareexchange returns the success code.

На других платформах / компиляторах используйте соответствующую встроенную функцию для инструкции LOCK CMPXCHG, которую предоставляет _InterlockedCompareExchange от MS.

3 голосов
/ 18 сентября 2008

Строго говоря, вам нужно подождать, пока C ++ 0x сможет написать потокобезопасный код на чистом C ++.

На данный момент вы можете использовать Posix или создавать свои собственные независимые от платформы обертки для сравнения и обмена и / или взаимосвязанного увеличения / уменьшения.

2 голосов
/ 08 декабря 2008

Win32 InterlockedIncrementAcquire и InterlockedDecrementRelease (если вы хотите быть в безопасности и заботиться о платформах с возможным переупорядочением, следовательно, вам необходимо одновременно создавать барьеры памяти) или InterlockedIncrement и InterlockedDecrement (если вы уверены, что останетесь x86), атомная и сделает работу.

Тем не менее, Boost / TR1 shared_ptr <> будет обрабатывать все это за вас, поэтому, если вам не нужно реализовать это самостоятельно, вы, вероятно, приложите все усилия, чтобы придерживаться его.

1 голос
/ 08 декабря 2008

Имейте в виду, что блокировка очень дорога, и это происходит каждый раз, когда вы перемещаете объекты между умными указателями - даже когда объект в данный момент принадлежит одному потоку (библиотека умных указателей этого не знает).

Учитывая это, здесь может применяться эмпирическое правило (я рад, что меня поправили!)

Если к вам применимо следующее:

  • У вас есть сложные структуры данных, для которых было бы сложно написать деструкторы (или там, где семантика значений в стиле STL была бы неприемлемой по своему замыслу), поэтому вам нужны умные указатели, чтобы сделать это за вас, и
  • Вы используете несколько потоков, совместно использующих эти объекты, и
  • Вы заботитесь о производительности и правильности

... тогда фактическая сборка мусора может быть лучшим выбором. Хотя у GC плохая репутация по производительности, все относительно. Я считаю, что это очень выгодно по сравнению с блокировкой умных указателей. Это была важная часть того, почему команда CLR выбрала настоящий GC вместо того, чтобы использовать подсчет ссылок. См. эту статью , в частности это абсолютное сравнение того, что означает справочное присвоение, если у вас идет подсчет:

без пересчета:

a = b;

подсчет ссылок:

if (a != null)
    if (InterlockedDecrement(ref a.m_ref) == 0)
            a.FinalRelease();

if (b != null)
    InterlockedIncrement(ref b.m_ref);

a = b;
0 голосов
/ 24 сентября 2008

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

В частности, если вы не можете гарантировать, что интеллектуальный указатель не изменится при назначении другому интеллектуальному указателю, вы делаете это неправильно или делаете что-то очень ненадежное с самого начала. Если интеллектуальный указатель может измениться при назначении другому интеллектуальному указателю, это означает, что код, выполняющий назначение, не владеет интеллектуальным указателем, который, как подозревают, начинается с.

0 голосов
/ 18 сентября 2008

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

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

Вопрос эффективности: библиотека pthread настолько эффективна, насколько это возможно, и при этом гарантирует, что блокировка мьютекса является атомарной для вашей ОС.

Это дорого: наверное. Но для всего, что требует гарантии, есть стоимость.

...