Потокобезопасность класса для нескольких читателей / одного писателя - PullRequest
4 голосов
/ 07 февраля 2012

Я работаю над набором, который часто читают, но редко пишут.

class A {
  boost::shared_ptr<std::set<int> > _mySet;
public:
  void add(int v) {
    boost::shared_ptr<std::set<int> > tmpSet(new std::set<int>(*_mySet));
    tmpSet->insert(v);  // insert to tmpSet
    _mySet = tmpSet;    // swap _mySet
  }
  void check(int v) {
    boost::shared_ptr<std::set<int> > theSet = _mySet;
    if (theSet->find(v) != theSet->end()) {
      // do something irrelevant
    }
  }
};

В классе add() вызывается только одним потоком, а check() вызывается многими потоками. check() не волнует, является ли _mySet самым последним или нет. Класс потокобезопасен? Возможно ли, что поток, выполняющий check(), будет наблюдать swap _mySet, происходящее до insert to tmpSet?

Ответы [ 3 ]

2 голосов
/ 07 февраля 2012

Это интересное использование shared_ptr для обеспечения безопасности потоков.То, в порядке ли это, зависит от гарантий безопасности потока boost::shared_ptr.В частности, устанавливает ли он какой-то забор или мембрану, чтобы вы гарантировали, что все записи в конструкторе и insert функциях set происходят до того, как какая-либо модификация значения указателя станет видимой.

Я могу найти no потокобезопасность гарантирует что бы то ни было в документации Boost для умных указателей.Это удивляет меня, так как я был уверен, что некоторые из них.Но быстрый взгляд на источники для 1.47.0 ничего не показывает, и любое использование boost::shared_ptr в многопоточной среде завершится неудачей.(Может кто-нибудь указать мне, что мне не хватает. Я не могу поверить, что boost::shared_ptr проигнорировал многопоточность.)

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

2 голосов
/ 07 февраля 2012

Вам нужна синхронизация, она не является поточно-ориентированной.Обычно это не имеет значения, даже такая простая вещь, как shared += value;, не является поточно-ориентированной.

посмотрите, например, что касается безопасности потоков в shared_ptr: Является ли boost shared_ptr безопасным для потоков?

Я бы также поставил под сомнение ваше распределение / замену в add() и использование shared_ptr в check()

обновлении:

Я вернулся и повторно переписал dox для shared_ptr ... Скорее всего, он ориентирован на многопоточность, так какподсчет ссылок для shared_ptr является поточно-ориентированным.Однако вы делаете (ИМХО) ненужную сложность, не используя блокировку чтения / записи.

0 голосов
/ 07 февраля 2012

В конце концов этот код должен быть потокобезопасным:

atomic_store(&_my_set,tmpSet);

и

theSet = atomic_load(&_mySet);

(вместо простых заданий)

Но я не знаю текущего статуса поддержки атомарности для shared_ptr.

Обратите внимание, что добавление атомарности в shared_ptr без блокировок действительно сложная вещь; так что даже атомарность реализована, она может ретранслировать мьютексы или спин-блокировки пользовательского режима и, следовательно, иногда может страдать из-за проблем с производительностью

Редактировать: Возможно, также следует добавить изменчивый квалификатор для переменной-члена _my_set ... но я не уверен, что это строго требуется семантикой атомарных операций

...