Как реализовать потокобезопасный подсчет ссылок в C++

StackOverflow https://stackoverflow.com/questions/93073

Вопрос

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

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

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

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

Это было полезно?

Решение

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

  • У вас есть сложные структуры данных, для которых было бы сложно написать деструкторы (или где семантика значений в стиле 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;

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top