Правильный способ создать thread_safe shared_ptr без блокировки?

Я пытаюсь создать класс с потокобезопасным shared_ptr. Мой пример использования заключается в том, что shared_ptr принадлежит объекту класса и ведет себя как единый объект (функция CreateIfNotExist может быть запущена любым потоком в любой момент времени).

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

Вот что у меня есть (обратите внимание, что единственная рассматриваемая функция - это функция CreateIfNotExist(), остальное - для тестирования):

#include <memory>
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>

struct A {
    A(int a) : x(a) {}
    int x;
};

struct B {
    B() : test(nullptr) {}

    void CreateIfNotExist(int val) {
        std::shared_ptr<A> newPtr = std::make_shared<A>(val);
        std::shared_ptr<A> _null = nullptr;
        std::atomic_compare_exchange_strong(&test, &_null, newPtr);
    }

    std::shared_ptr<A> test;
};

int gRet = -1;
std::mutex m;

void Func(B* b, int val) {
    b->CreateIfNotExist(val);
    int ret =  b->test->x;

    if(gRet == -1) {
        std::unique_lock<std::mutex> l(m);
        if(gRet == -1) {
            gRet = ret;
        }
    }

    if(ret != gRet) {
        std::cout << " FAILED " << std::endl;
    }
}

int main() {
    B b;

    std::vector<std::thread> threads;
    for(int i = 0; i < 10000; ++i) {
        threads.clear();
        for(int i = 0; i < 8; ++i) threads.emplace_back(&Func, &b, i);
        for(int i = 0; i < 8; ++i) threads[i].join();
    }
}

Это правильный способ сделать это? Есть ли лучший способ убедиться, что все потоки, вызывающие CreateIfNotExist() одновременно, используют один и тот же shared_ptr?

1 ответ

Решение

Возможно, что-то в этом роде:

struct B {
  void CreateIfNotExist(int val) {
    std::call_once(test_init,
                   [this, val](){test = std::make_shared<A>(val);});
  }

  std::shared_ptr<A> test;
  std::once_flag test_init;
};
Другие вопросы по тегам