是否使用类包装器对符合 C++11 的对象进行线程安全访问
Is using the class-wrapper for thread-safe access to the object conform to C++11?
我是否可以使用此类包装器对对象的线程安全访问,并且所需的行为符合C++11?
琴弦的主要重音:
T* operator->() {
和
T& operator*() {
请注意,我知道这里最好使用 std::atomic<> 作为整数(int(,但在此代码中,我们可以使用任何其他对象而不是 int。
使用执行指针习惯用法的版本 2.0:
#include<thread>
#include<mutex>
#include<memory>
#include<iostream>
#include<vector>
template<typename T>
class safe_obj {
T obj;
mutable std::mutex mtx;
safe_obj(safe_obj<T> &) {}
safe_obj<T>& operator=(safe_obj<T> &) {}
class T_exclusive_lock {
std::unique_lock<std::mutex> xlock;
public:
T*const self;
T_exclusive_lock(T * const s, std::mutex& _mtx)
:self(s), xlock(_mtx) {}
T* operator -> () const {return self;}
operator T&() {return *self;}
friend std::ostream& operator << (std::ostream &stream, T_exclusive_lock &obj) {
stream << obj;
return stream;
}
};
public:
template<typename Types>
safe_obj(Types args) : obj(args )
{ }
T_exclusive_lock operator->() {
return T_exclusive_lock(&obj, mtx);
}
T_exclusive_lock* operator*() {
return &T_exclusive_lock(&obj, mtx);
}
};
int main() {
safe_obj<std::shared_ptr<int> > safe_int( std::make_shared<int>(10) );
auto lambda = [&safe_int]() {
std::cout << safe_int->use_count() << std::endl; // is that thread-safe?
std::cout << *safe_int << std::endl; // is that thread-safe?
std::cout << *safe_int->get() << std::endl; // is that thread-safe?
};
std::vector<std::thread> thr_grp;
for(size_t i = 0; i < 10; ++i) thr_grp.emplace_back(std::thread(lambda));
for(auto &i : thr_grp) i.join();
int b; std::cin >> b;
return 0;
}
您提供的原始代码不保证任何线程安全。一旦std::unique_lock
超出范围,即在使用要保护的对象之前,即可解锁互斥锁。
为了达到预期的结果,您需要声明另一个模板化类(例如 locked_obj<T>
(,它将表示处于锁定状态的对象(通过在safe_obj
的互斥体上有一个unique_lock
(,并从重载运算符返回这样的对象。这样的对象将是临时的,允许您在locked_obj
的生命周期内操作受保护的对象。由于临时对象一直存在到当前语句结束,因此使用此类safe_obj
将基本上是透明的。
此技术是绕过执行指针习惯用法的应用。
锁定单个函数或锁定对单个对象的访问并不能保证线程安全;根据程序的不同,许多操作需要对同一对象进行多个函数调用而不会中断,或者需要对多个对象进行操作而不会中断。线程安全必须设计到应用程序中;没有库黑客会使未正确设计的应用程序成为线程安全的。
不,这不是线程安全的。当访问器函数返回时,互斥锁已解锁,因此对对象的访问不同步。
一种方法是重载赋值和转换运算符:
safe_obj & operator=(T const &t) {
std::unique_lock<std::mutex> lock(mtx);
obj = t;
return *this;
}
operator T() {
std::unique_lock<std::mutex> lock(mtx);
return obj;
}
但是,如果要提供所有复合赋值运算符,这可能会变得乏味,并且如果对象不可复制,则转换运算符将不起作用。
另一种方法是返回一个包含 unique_lock
的访问器对象,只要您有权访问该对象,它就会锁定互斥锁。
相关文章:
- 即发即弃 std::线程对象清理自身
- C++:处理线程本地对象销毁
- 结束另一个线程中使用的对象的生存期
- 通过插槽和信号在不同线程中的两个qt对象之间进行通信
- 处理影响跨不同线程共享对象的定时回调的最佳方法是什么?
- 线程调用的函数对对象删除是否安全?
- 将对象传递给多线程对象 Qt
- asio 链对象线程安全吗?
- 如何初始化大线程本地对象?
- 在销毁期间从另一个线程调用对象上调用方法是否未定义行为?
- 当只有一个线程主要使用该对象而其他线程很少使用它时,如何最小化该对象的互斥锁锁定?
- 如何将对象从类线程传递到帖子?
- 将抽象对象从主线程发送到其他线程
- 在 While 循环中重用对象的多线程
- 本地静态函数对象线程安全的初始化
- 对于 c++11 之前的编译器,返回本地静态对象线程是否安全
- 从一个对象线程中使用 const 方法是否安全
- C++流对象线程安全
- 使STL对象线程安全的标准方法
- QT多线程和移动对象线程创建后