gpt4 book ai didi

c++ - 一个作者中的 std::shared_ptr 许多读者设计是线程安全的吗?

转载 作者:搜寻专家 更新时间:2023-10-31 00:51:14 26 4
gpt4 key购买 nike

在多线程服务器中,一个线程(写入者)定期从数据库更新数据,其他线程(读取者)使用这些数据处理用户的请求。

我尝试使用读/写锁来满足这个请求,但是性能太差了,所以需要找别的东西。

我读自https://en.cppreference.com/w/cpp/memory/shared_ptr ,它说:

所有成员函数(包括复制构造函数和复制赋值)都可以由多个线程在 shared_ptr 的不同实例上调用,无需额外的同步即使这些实例是同一对象的拷贝并共享所有权

然后经过一些研究,我使用 std::shared_ptr 来做到这一点。代码如下所示。

// this class is singleton
class DataManager{
public:
// all the reader thread use this method to get data and release shared_ptr
// at the end of user's request
std::shared_ptr<Data> get_main_ptr(){
return _main_data;
}
private:
// data1
std::shared_ptr<Data> _main_data;
// data2
std::shared_ptr<Data> _back_data;

// read database, write data in to _data
void update_data(std::shared_ptr<Data> _data);

// this function called at a separate thread every 10 min
bool reload_data(){
// write data in back pointer
update_data(_back_data);

//save the _main_data
std::shared_ptr<Data> old_ptr = _main_data;

//exchange pointer, reader thread hold the copy of _main_data
_main_data = _back_data;

// wait until reader threads release all copy of _main_data
while(old_ptr.use_count() != 1) {
sleep(5);
}

// clear the data
old_ptr->clear();
_back_data = old_ptr;
return;
}

}

这个方法似乎在生产环境中有效。但我不太确定也不了解shared_ptr的线程安全级别。这个方法有问题吗?或者其他满足我要求的建议

最佳答案

您似乎重新分配了一个 shared_ptr在线程之间共享:

_main_data = _back_data;

如果另一个线程读取或复制 _main_data同时它可能会得到一个损坏的拷贝。

分配给 shared_ptr不是线程安全的,因为 shared_ptr包含两个指针成员,它们不能同时被原子更新。参见 shared_ptr :

If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur;

要修复该竞争条件,代码需要使用 atomic_store :

atomic_store(&_main_data, _back_data);

读者必须做到:

auto main_data = atomic_load(&_main_data);

Notes section有帮助:

These functions are typically implemented using mutexes, stored in a global hash table where the pointer value is used as the key.

To avoid data races, once a shared pointer is passed to any of these functions, it cannot be accessed non-atomically. In particular, you cannot dereference such a shared_ptr without first atomically loading it into another shared_ptr object, and then dereferencing through the second object.

The Concurrency TS offers atomic smart pointer classes atomic_shared_ptr and atomic_weak_ptr as a replacement for the use of these functions.

Since C++20: These functions were deprecated in favor of the specializations of the std::atomic template: std::atomic<std::shared_ptr> and std::atomic<std::weak_ptr>.


另外,你应该制作Data析构函数执行所有清理工作,这样您就不必等到读取线程释放 _main_data手动清理它。


或者,您可以使用 std::atomicboost::intrusive_ptr使更新数据指针线程安全、原子、无等待和无泄漏。

使用 boost::intrusive_ptr 的好处而不是 std::shared_ptr是前者可以从普通指针线程安全地创建,因为原子引用计数存储在对象内部。

工作示例:

#include <iostream>
#include <atomic>

#include <boost/smart_ptr/intrusive_ptr.hpp>
#include <boost/smart_ptr/intrusive_ref_counter.hpp>

struct Data
: boost::intrusive_ref_counter<Data, boost::thread_safe_counter>
{};

using DataPtr = boost::intrusive_ptr<Data>;

class DataAccessor
{
std::atomic<Data*> data_ = 0;

public:
~DataAccessor() {
DataPtr{data_.load(std::memory_order_acquire), false}; // Destroy data_.
}

DataPtr get_data() const {
return DataPtr{data_.load(std::memory_order_acquire)};
};

void set_data(DataPtr new_data) {
DataPtr old_data{data_.load(std::memory_order_relaxed), false}; // Destroy data_.
data_.store(new_data.detach(), std::memory_order_release);
}
};

int main() {
DataAccessor da;

DataPtr new_data{new Data};
da.set_data(new_data);
DataPtr old_data = da.get_data();
std::cout << (new_data == old_data) << '\n';
}

valgrind运行:

$ valgrind ./test
==21502== Memcheck, a memory error detector
==21502== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==21502== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==21502== Command: ./test
==21502==
1
==21502==
==21502== HEAP SUMMARY:
==21502== in use at exit: 0 bytes in 0 blocks
==21502== total heap usage: 4 allocs, 4 frees, 73,736 bytes allocated
==21502==
==21502== All heap blocks were freed -- no leaks are possible
==21502==
==21502== For counts of detected and suppressed errors, rerun with: -v
==21502== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

关于c++ - 一个作者中的 std::shared_ptr 许多读者设计是线程安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55693819/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com