共享指针控制块的线程安全

Thread Safety of Shared Pointers' Control Block

我正在开发一个使用共享指针的小程序。我有一个简单的 class“事物”,它只是一个具有整数属性的 class:


class Thing{
public:
    Thing(int m){x=m;}
    int operator()(){
        return x;
    }
    void set_val(int v){
        x=v;
    }
    int x;
    ~Thing(){
        std::cout<<"Deleted thing with value "<<x<<std::endl;
    }

};

我有一个简单的函数“fun”,它接受一个 shared_ptr 实例和一个整数值 index,它只是跟踪哪个线程正在输出给定值。该函数打印出传递给函数的索引值,以及作为参数传入的共享指针的引用计数

std::mutex mtx1;
void fun(std::shared_ptr<Thing> t1,int index){
    std::lock_guard <std::mutex> loc(mtx1);
    int m=t1.use_count();
    std::cout<<index<<" : "<<m<<std::endl;
}

在 main 中,我创建了一个共享指针的实例,它是一个 Thing 对象的包装器,如下所示:

    std::shared_ptr<Thing> ptr5(nullptr);
    ptr5=std::make_shared<Thing>(110);

(为了异常安全而以这种方式声明)。

然后我创建了 3 个线程,每个线程都创建了一个执行 fun() 函数的线程,该函数将 ptr5 共享指针作为参数并增加索引值:

    std::thread t1(fun,ptr5,1),t2(fun,ptr5,2),t3(fun,ptr5,3);
    t1.join();
    t2.join();
    t3.join();

我在这里的思考过程是,由于每个共享指针控制块成员函数都是线程安全的,因此在 fun() 函数中对 use_count() 的调用不是原子指令,因此不需要锁定。然而,运行 没有和没有 lock_guard,这仍然会导致竞争条件。我希望看到输出:

1:2 2:3 3:4

由于每个线程都会生成对原始共享指针的新引用,因此 use_count() 每次都会递增 1 个引用。但是,由于某些竞争条件,我的输出仍然是随机的。

在多线程环境中,use_count() 是近似值。来自 cppreference :

In multithreaded environment, the value returned by use_count is approximate (typical implementations use a memory_order_relaxed load)

shared_ptr 的控制块在其他方面是线程安全的:

All member functions (including copy constructor and copy assignment) can be called by multiple threads on different instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of the same object.

看到过时的 use_count() 并不表示控制块已被竞争条件损坏。

请注意,这不会扩展到修改指向的对象。它不为指向的对象提供同步。只有 shared_ptr 和控制块的状态受到保护。