Связывание pthread отключает реализацию shared_ptr без блокировки

Название в значительной степени передает всю необходимую информацию, но вот минимальное воспроизведение:

#include <atomic>
#include <cstdio>
#include <memory>

int main() {
    auto ptr = std::make_shared<int>(0);
    bool is_lockless = std::atomic_is_lock_free(&ptr);
    printf("shared_ptr is lockless: %d\n", is_lockless);
}

Компиляция со следующими параметрами компилятора дает реализацию shared_ptr без блокировки:

g++ -std=c++11 -march=native main.cpp

Пока это не так:

g++ -std=c++11 -march=native -pthread main.cpp

Версия GCC: 5.3.0 (в Linux, с использованием libstdc++), проверена на нескольких машинах, которые должны иметь необходимые атомарные инструкции для выполнения этой работы.

Есть ли способ заставить реализацию без блокировки (мне нужна версия без блокировки, независимо от производительности)?


person Gustorn    schedule 17.02.2016    source источник


Ответы (2)


Если вы используете shared_ptr в многопоточном окружении, вам НЕОБХОДИМО иметь блокировки [какого-то рода — они могут быть реализованы как атомарное увеличение и уменьшение, но могут быть места, где требуется «большая» блокировка, чтобы гарантировать отсутствие гонок]. Версия без блокировки работает только тогда, когда есть только один поток. Если вы не используете темы, не связывайтесь с -lpthread.

Я уверен, что есть какой-то хитрый способ убедить компилятор, что вы ДЕЙСТВИТЕЛЬНО не используете потоки для ваших общих указателей, но вы ДЕЙСТВИТЕЛЬНО находитесь на хрупкой территории, если вы это сделаете - что произойдет, если shared_ptr будет передан потоку? Вы можете быть в состоянии гарантировать это СЕЙЧАС, но кто-то, вероятно, случайно или намеренно введет его во что-то, что работает в другом потоке, и все это сломается.

person Mats Petersson    schedule 18.02.2016

Есть две отдельные вещи:

  • Манипуляции со счетчиком ссылок в блоке управления (или аналогичном элементе) обычно реализуются с помощью атомных элементов без блокировки всякий раз, когда это возможно. Это не то, что говорит вам std::atomic_is_lock_free.

    • __shared_ptr в libstdc++ шаблон политики блокировки, поэтому вы можете явно использовать

      template<typename T>
      using shared_ptr_unsynchronized = std::__shared_ptr<T, __gnu_cxx::_S_single>;
      

      если вы знаете, что делаете.

  • std::atomic_is_lock_free сообщает вам, являются ли атомарные функции доступа (std::atomic_{store, load, exchange, compare_exchange} и т. д.) на shared_ptr неблокируемыми. Эти функции используются для одновременного доступа к одному и тому же объекту shared_ptr, и типичные реализации будут использовать мьютекс.
person T.C.    schedule 18.02.2016