在没有锁的情况下创建 thread_safe shared_ptr 的正确方法?
Correct way to create thread_safe shared_ptr without a lock?
我正在尝试创建一个具有线程安全 shared_ptr 的 class。我的用例是 shared_ptr 属于 class 的一个对象,并且表现得有点像单例(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?
也许是这样的:
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;
};
我正在尝试创建一个具有线程安全 shared_ptr 的 class。我的用例是 shared_ptr 属于 class 的一个对象,并且表现得有点像单例(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?
也许是这样的:
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;
};