关于共享_ptr的种族条件示例

发布于 2025-01-30 17:21:42 字数 1112 浏览 1 评论 0 原文

https://godbolt.org/z/neypyqdqk”中没有种族条件

#include<memory>
#include<thread>

std::shared_ptr<int> g_s = std::make_shared<int>(1);

void f1(std::shared_ptr<int> sp)
{
    std::shared_ptr<int>l_s1 = sp; // read g_s
}

void f2()
{
    std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
    std::thread th(f1, g_s);
    th.detach();
    g_s = l_s2; // write g_s
}

int main()
{
    std::thread(f2).join();
}

为什么在代码snippet 下面?

#include<memory>
#include<thread>

std::shared_ptr<int> g_s = std::make_shared<int>(1);

void f1(std::shared_ptr<int>& sp)
{
    std::shared_ptr<int>l_s1 = sp; // read g_s
}

void f2()
{
    std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
    std::thread th(f1, std::ref(g_s));
    th.detach();
    g_s = l_s2; // write g_s
}

int main()
{
    std::thread(f2).join();
}

我目前对这个问题的想法是在第一个答案中看到的。但是我还不那么确定。 有人可以阐明这个问题吗?

Why there is no race condition in the code snippet below,

#include<memory>
#include<thread>

std::shared_ptr<int> g_s = std::make_shared<int>(1);

void f1(std::shared_ptr<int> sp)
{
    std::shared_ptr<int>l_s1 = sp; // read g_s
}

void f2()
{
    std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
    std::thread th(f1, g_s);
    th.detach();
    g_s = l_s2; // write g_s
}

int main()
{
    std::thread(f2).join();
}

whereas there is a race condition in the code snippet below?

#include<memory>
#include<thread>

std::shared_ptr<int> g_s = std::make_shared<int>(1);

void f1(std::shared_ptr<int>& sp)
{
    std::shared_ptr<int>l_s1 = sp; // read g_s
}

void f2()
{
    std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
    std::thread th(f1, std::ref(g_s));
    th.detach();
    g_s = l_s2; // write g_s
}

int main()
{
    std::thread(f2).join();
}

My current thought about this question is seen at the first answer. But I am not so sure yet.
Could somebody please shed some light this matter?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

等风来 2025-02-06 17:21:42

std :: shardy_ptr 本身不是线程 - safe )std :: shared_ptr&lt;&gt;不能从两个线程同一时间修改。

它不是线程安全的,因为为了速度而言,在共享_ptr&gt;&gt;&gt;&gt;&gt;中没有静音。

您的第一个代码片段仅通过共享_ptr&lt; gt;实例,切勿引用,因此两个线程修改了它们的不同副本。线程安全。

第二个代码段将引用到第二个线程,因此两个线程修改了共享_ptr&lt; gt;的同一实例这不是线程安全的。

为什么修改共享指针的副本是线程安全的?我们需要研究如何实施。

共享指针由对象的指针本身和指向连接两个计数器的结构的指针组成,一个计数共享指针的参考数量,第二个计数弱指针参考的数量。计数器通常在堆上分配,因此可以在共享指针的几个副本之间共享指向相同堆的计数器的指针。

当制作共享指针的副本时,将堆指向其计数器的堆复制到共享指针的其他副本,而共享指针计数器会增加原子上

当共享指针的副本被破坏时,其计数器在原子上被减少。如果这是最后的副本,则将计数器从堆中划分。

为什么应在原子上逐渐增加/减少计数器?由于可能构造或破坏其他线程中的其他共享指针的其他副本,因此其他线程会增加/减少计数器。

如果计数器在同一时间上的不同线程上增加/减少,则应在原子上进行,以便将值读取为一个单个操作。这是避免反击本身内的比赛条件所需的。

原子操作通常是在

std::shared_ptr itself is not thread-safe, meaning that Same object (instance) of std::shared_ptr<> can't be modified same time from two threads.

It is not thread safe because there is no mutex inside implementation of shared_ptr<>, for the sake of speed.

Your first code snippet passes around only copies of shared_ptr<> instance, never references, hence two threads modify different copies of them. Which is thread safe.

Second code snippet passes reference to second thread, hence two threads modify same instance of shared_ptr<> which is not thread safe.

Why modifying a copy of shared pointer is thread safe? We need to look into how it is implemented.

Shared pointer consists of object's pointer itself and the pointer which points to the structure that conatins two counters, one counts number of references of shared pointer, second counts number of weak pointer references. Counters are usually allocated on Heap, so that pointer to same heaped counters can be shared between several copies of shared pointer.

When copy of shared pointer is made, heap pointer to its counters is copied to other copy of shared pointer, and shared pointer counter is incremented Atomically.

When copy of shared pointer is destroyed, its counter is decremented Atomically. And if it was very last copy then counters are deallocated from heap.

Why counter should be incremented/decremented Atomically? Because same time other copy of shared pointer in other thread might be constructed or destroyed, hence same time other thread will increment/decrement counter.

If counter is incremented/decremented on different threads same time, then it should happen Atomically, so that value is read-modified-stored as one single operation. This is needed to avoid race conditions within counter itself.

Atomic operations are usually done with the help of std::atomic.

歌枕肩 2025-02-06 17:21:42

首先, std :: shared_ptr 确保访问基础控制块是安全的。

对于后一个代码段,可以同时读取并写入不同线程的原始指针和指向控制块的指针,这将构成竞赛。

对于前一个,因为 void f1(std :: shared_ptr&lt; int; int sp)通过值(即 std :: shared_ptr&lt; t&gt; sp )通过值,读取原始指针和 sp 控制块的指针都独立于将所述指针分配给 g_s 。因此没有数据竞赛条件。

Firstly, std::shared_ptr guarantees access to underlying control block is thread safe.

For the latter code snippet, both the raw pointer and the pointer to the control block may be concurrently read from and written to by different threads, which would constitute a race.

For the former one, since void f1(std::shared_ptr<int> sp) passes the parameter(i.e std::shared_ptr<T> sp) by value, reading both the raw pointer and the pointer to the control block of sp is independent from assigning the said pointers to g_s. So there is no data race condition.

写下不归期 2025-02-06 17:21:42

为了帮助想要更好地了解 shared_ptr 的数据竞赛的读者。

代码代码片段没有任何种族条件:

#include<memory>
#include<thread>

std::shared_ptr<int> g_s = std::make_shared<int>(1);
std::weak_ptr<int> w_p{g_s};

void f1(std::weak_ptr<int>& wp)
{
    std::shared_ptr<int>l_s1 = wp.lock(); // read g_s
}

void f2()
{
    std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
    std::thread th(f1, std::ref(w_p));
    th.detach();
    g_s = l_s2; // write g_s
}

int main()
{
    std::thread(f2).join();
}

下面的代码段也没有任何种族条件:

#include<memory>
#include<thread>

std::shared_ptr<int> g_s = std::make_shared<int>(1);
std::weak_ptr<int> w_p{g_s};

void f1(std::weak_ptr<int> wp)
{
    std::shared_ptr<int>l_s1 = wp.lock(); // read g_s
}

void f2()
{
    std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
    std::thread th(f1, w_p);
    th.detach();
    g_s = l_s2; // write g_s
}

int main()
{
    std::thread(f2).join();
}

因为 std :: feek_ptr :: feek_ptr :: lock() 创建一个新的std :: shared_ptr,该shared_ptr共享托管对象的所有权。如果没有托管对象,即 *这是空的,那么返回的shared_ptr也为空。 wp.lock()返回临时 sharone_ptr ,这与名为 g_s 的一个不同。

To help the readers who want to better understand about the data race about shared_ptr.

This code snippet does not have any race condition:

#include<memory>
#include<thread>

std::shared_ptr<int> g_s = std::make_shared<int>(1);
std::weak_ptr<int> w_p{g_s};

void f1(std::weak_ptr<int>& wp)
{
    std::shared_ptr<int>l_s1 = wp.lock(); // read g_s
}

void f2()
{
    std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
    std::thread th(f1, std::ref(w_p));
    th.detach();
    g_s = l_s2; // write g_s
}

int main()
{
    std::thread(f2).join();
}

This code snippet below does not have any race condition, either:

#include<memory>
#include<thread>

std::shared_ptr<int> g_s = std::make_shared<int>(1);
std::weak_ptr<int> w_p{g_s};

void f1(std::weak_ptr<int> wp)
{
    std::shared_ptr<int>l_s1 = wp.lock(); // read g_s
}

void f2()
{
    std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
    std::thread th(f1, w_p);
    th.detach();
    g_s = l_s2; // write g_s
}

int main()
{
    std::thread(f2).join();
}

Since the std::weak_ptr::lock() creates a new std::shared_ptr that shares ownership of the managed object. If there is no managed object, i.e. *this is empty, then the returned shared_ptr also is empty., wp.Lock() returns a temporary shared_ptr, which is different from the one named g_s.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文