提问人:John 提问时间:10/8/2023 最后编辑:John 更新时间:10/9/2023 访问量:79
当共享的托管对象永远不会同时被访问时,在生产者和使用者之间使用 std::shared_ptr 是否安全?
Is it safe to use std::shared_ptr between productor and consumer when the shared managed object would never be acessed at the same time?
问:
下面是演示代码片段。
是否可以删除它,因为它本身是线程安全的,并且生产者和消费者永远不会同时访问托管对象?mutex
std::shared_ptr
#include <memory>
#include <mutex>
#include <thread>
struct Demo {
int i;
float f;
};
using SCDemoPtr = std::shared_ptr<Demo>;
class ResultManager {
public:
ResultManager();
void SetFrontRes(SCDemoPtr oms_result);
void SetBackRes(SCDemoPtr oms_result);
void MergeResults();
private:
std::mutex front_mutex;
std::mutex back_mutex;
SCDemoPtr front_result;
SCDemoPtr back_result;
};
ResultManager::ResultManager() {}
void ResultManager::SetFrontRes(SCDemoPtr oms_res) {
std::lock_guard<std::mutex> guard(front_mutex);
front_result = oms_res;
}
void ResultManager::SetBackRes(SCDemoPtr oms_res) {
std::lock_guard<std::mutex> guard(back_mutex);
back_result = oms_res;
}
void ResultManager::MergeResults() {
SCDemoPtr cur_front_res;
SCDemoPtr cur_back_res;
{
std::lock_guard<std::mutex> guard(front_mutex);
cur_front_res = front_result;
}
{
std::lock_guard<std::mutex> guard(back_mutex);
cur_back_res = back_result;
}
if (cur_front_res == nullptr || cur_back_res == nullptr) {
return;
}
SCDemoPtr merge_res(new Demo);
merge_res->i = cur_front_res->i + cur_back_res->i;
}
int main() {
ResultManager res_manager;
std::thread producer1([&res_manager]() {
while (true) {
SCDemoPtr res(new Demo);
res->i = 1;
res->f = 3.14;
res_manager.SetFrontRes(res);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
});
std::thread producer2([&res_manager]() {
while (true) {
SCDemoPtr res(new Demo);
res->i = 1;
res->f = 3.14;
res_manager.SetBackRes(res);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
});
while (true) {
res_manager.MergeResults();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
producer1.join();
producer2.join();
return 0;
}
答:
是否可以删除互斥锁,因为 std::shared_ptr 本身是线程安全的
不,对 本身的访问不是线程安全的,并且行为与访问任何其他对象没有任何不同。当另一个线程尝试访问它时尝试修改它是一种数据争用,并导致未定义的行为。std::shared_ptr
线程安全的唯一一点是托管对象的生存期,它保证被一个线程销毁。std::shared_ptr
评论
MergeResults()
producer
producer2
(cur_front_res == nullptr || cur_back_res == nullptr)
不,这不安全。您将不同级别的线程安全混为一谈:
指向对象的线程安全。这将由 确保。(不是你的情况)
std::shared_ptr<std::atomic<T>>
引用计数器的线程安全。这是由任何 .这允许您让多个线程同时使用拥有相同线程的单独对象。(不是你的情况)
std::shared_ptr<T>
std::shared_ptr<T>
T
智能指针本身的线程安全。这是由 std::
atomic<std::shared_ptr<T>>
或带有保护它的 a 来确保的。(您的情况)std::shared_ptr
std::mutex
您正在一个地方使用和写入 在另一个地方。这只是线程安全的,因为你要确保互斥。如果删除互斥锁,则会出现数据争用。back_result
cur_back_res = back_result;
back_result = oms_res;
您使用的是 C++14,因此 std::atomic<std::shared_ptr>
专业化不可用,但您可以使用旧的无实用程序函数进行原子操作:
// atomically load from a std::shared_ptr
cur_back_result = std::atomic_load(&back_result);
// atomically store to a std::shared_ptr
std::atomic_store(&back_result, &oms_res);
如果使用这些函数,则不再需要互斥锁来保护 .std::shared_ptr
注意:这些函数的显式
版本还允许您指定内存顺序。如果您不熟悉此主题,请参阅每个memory_order是什么意思?
Смотритетакже: std::shared_ptr 和 std::atomic<std::shared_ptr> 又名 std::experimental::atomic_shared_ptr有什么区别?
评论
std::shared_ptr
评论