非静态数据成员的 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 数据,再次的主要内容,如果未能解决你的问题,请参考以下文章