非静态数据成员的 thread_local 数据,再次

Posted

技术标签:

【中文标题】非静态数据成员的 thread_local 数据,再次【英文标题】:thread_local data for non static data members, once more 【发布时间】:2016-07-19 13:24:43 【问题描述】:

问题

c++11 引入了thread_local,它提供线程本地数据,但不能与非静态数据成员一起使用。

这就引出了问题:Why may thread_local not be applied to non-static data members and how to implement thread-local non-static data members?

答案是:

...我建议使用boost::thread_specific_ptr。您可以将其中之一放入您的班级并获得您想要的行为。

boost::thread_specific_ptr 析构函数附加了以下note:

与此 thread_specific_ptr 关联的所有线程特定实例(可能与此线程关联的实例除外)必须为空。

有没有办法解决这个问题? 我需要用于非静态数据成员的线程本地存储,这将在销毁时释放所有线程数据,即使仍有线程在运行(或至少不会在销毁时失败/泄漏的 tls)。 如果boost::thread_specific_ptr 不是正确的选择,我可以使用受保护的互斥锁std::vector 代替吗?


背景

我有一个从 mongodb 接收数据的 threadsafe 类。

class JsonTable

    public:
    std::string getData() const;
    //....
    private:
    ThreadLocalStorage<mongocxx::client> _clients;
    //....
;

mongocxx::client 不得在多个线程之间共享。 因此,为了使getData 线程安全,我需要为每个线程构造一个mongocxx::client。当我的JsonTable 类被破坏时,我希望所有客户端都被关闭/破坏,即使最初创建它们的线程仍在运行。

【问题讨论】:

【参考方案1】:

创建一个线程本地静态映射,从 ptr-to-this 包装 shared-ptr 到 shared-ptr to-data。

创建一个非静态的 shared-ptr 到 shared-ptr to-data 的同步列表。

按需填充线程本地映射。填充后,将其添加到实例列表中。

在对象销毁时,使用原子共享 ptr 操作从列表的所有元素中清除内部 shared_ptr。这将删除每个实例的线程本地数据。

双共享 ptr 的包装器也使用原子操作来清除内部共享 ptr。如果线程死亡,这会清除数据。

实例和线程共同构成一个共享 ptr(内部),其生命周期由外部共享 ptr 管理(因此当线程和实例都消失时它会死掉)。

唯一的同步发生在对象被销毁或新线程访问该对象时,这应该保持性能稳定;

地图条目(带有空数据)比实例更持久。如果你愿意,你可以花一些精力定期清理它们。如果您有许多瞬态实例与许多持久线程交互,则可能会出现问题。添加一个正在使用与已清除数据的原子计数器,当它变高时,当您添加新条目时,通过删除已清除的条目。

【讨论】:

感谢您的回答。我真的很惊讶 std 和 boost 都没有提供这样的类,因为您的解决方案需要的不仅仅是几行代码,而且我认为用例一点也不晦涩。 第一个问题:您确定只有在销毁对象或添加新线程时才会发生同步吗?我认为对 shared_ptr 之一的每次读取访问都必须通过 atomic_load .... @dream 我说的是列表上的同步,而不是共享 ptr 上的原子操作。是的,我怀疑对(内部)共享 ptr 的所有访问都必须是 benatomic 的。在使用上述内容之前,我至少会尝试证明正确性:以上内容只是经过头部调试,没有经过测试/证明是正确的。

以上是关于非静态数据成员的 thread_local 数据,再次的主要内容,如果未能解决你的问题,请参考以下文章

成员初始化器列表和非静态数据成员的默认成员初始化器有啥区别?

成员初始值设定项不命名非静态数据成员

访问父类的成员“非静态数据成员的使用无效”C++

关于静态与非静态之具体总结

非静态数据成员和一个定义规则

回调问题错误:非静态数据成员的使用无效