提问人:Sami 提问时间:6/30/2023 最后编辑:273KSami 更新时间:6/30/2023 访问量:205
何时应在多线程中使用带有 std::shared_ptr C++互斥锁?
When should I use a mutex with std::shared_ptr in C++ multithreading?
问:
std::shared_ptr<Dog> pd;
void F() {
pd = std::make_shared<Dog>("Smokey");
}
int main() {
std::thread t1(F);
std::thread t2(F);
t1.join();
t2.join();
return 0;
}
std::shared_ptr<Dog> pd(new Dog("Gunner"));
void F() {
std::shared_ptr<Dog> localCopy = pd;
}
int main() {
std::thread t1(F);
std::thread t2(F);
t1.join();
t2.join();
return 0;
}
在C++中,我知道 std::shared_ptr 对于阅读和复制是线程安全的。但是我对何时需要使用互斥锁来同步线程有点困惑。我有两个代码片段。在第一个线程中,std::shared_ptr 被多个线程修改。在第二个线程中,每个线程仅读取和复制共享指针。我是在两种情况下都需要互斥锁,还是只在第一种情况下需要互斥锁?为什么或者为什么不?
答:
如果您仔细识别所涉及的所有活动部分,则更容易理解这一点:
一个引用计数的对象,这是一个对象,在某个地方,这是你的
Dog
引用计数器本身,用于跟踪对引用计数对象的引用数
使用引用计数器的共享指针本身
这些都是离散的实体,需要分别考虑和评估。
根据经验,在 C++ 中,如果从多个执行线程访问一个对象,并且至少有一个执行线程以某种方式“修改”它,那么执行线程必须与该对象“同步”;除非对象是“线程安全的”。“synchronize” 是什么意思?嗯,它不仅仅意味着一个互斥锁,在某个地方;但对于这个实际的例子来说,这就是它的意思:你需要在某个地方的某个互斥锁上保持锁定的同时访问对象。
数据点:引用计数器是线程安全的。智能指针,又名不是。std::shared_ptr
pd = std::make_shared<Dog>("Smokey");
这将修改多个执行线程中的共享指针 。这需要同步,这不是线程安全的。您需要一个互斥锁。pd
std::shared_ptr<Dog> localCopy = pd;
这会复制 ,它不会修改它。这也兼顾了参考计数器,作为制作副本(和销毁)它的一部分。引用计数器是线程安全的。共享指针不会被修改,只会被访问。这是线程安全的。pd
评论
std::mutex
std::shared_ptr
std::atomic<std::shared_ptr>
Dog
std::atomic<std::shared_ptr>
std::shared_ptr
std::mutex
A 由三个部分组成:std::shared_ptr<Dog>
- 智能指针本身
std::shared_ptr
- 指向原子参考计数器
- 指向对象
Dog
您需要的同步形式取决于要同时修改的同步形式。
1 同时修改智能指针
// for example
pd = std::make_shared<Dog>("Smokey");
这不是线程安全的,因为多个线程同时修改相同的 .基本上有三个选项:std::shared_ptr
std::atomic<std::shared_ptr>
(自 C++20)std::shared_ptr
的独立原子函数(自 C++20 起已弃用)std::mutex
理想情况下,使用专门为 . 相比之下,效率会较低,使用起来也不太方便。std::shared_ptr
std::mutex
2 同时修改参考计数器
// for example
std::shared_ptr<Dog> localCopy = pd;
您在这里不需要或其他任何东西,因为它已经是线程安全的。复制 不会修改原始对象,并且会以线程安全的方式更新(原子)引用计数器。std::mutex
std::shared_ptr
3 同时修改指向对象
// for example
localCopy->woof();
如果你的成员函数不是线程安全的,你将需要使用一些同步来确保线程安全。 是一个很好的候选者,但您也可以考虑使用 .Dog
woof
std::mutex
std::atomic
评论
std::shared_ptr