[英]Thread Safety Of a single variable
我理解线程安全的概念。 我正在寻求在尝试保护单个变量时简化线程安全性的建议。
说我有一个变量:
double aPass;
我想保护这个变量,所以我创建了一个互斥锁:
pthread_mutex_t aPass_lock;
现在有两种很好的方法可以让我想到这样做,但它们都有令人讨厌的缺点。 第一个是创建一个线程安全类来保存变量:
class aPass {
public:
aPass() {
pthread_mutex_init(&aPass_lock, NULL);
aPass_ = 0;
}
void get(double & setMe) {
pthread_mutex_lock(aPass_lock);
setMe = aPass_
pthread_mutex_unlock(aPass_lock);
}
void set(const double setThis) {
pthread_mutex_lock(aPass_lock);
aPass_ = setThis;
pthread_mutex_unlock(aPass_lock);
}
private:
double aPass_;
pthread_mutex_t aPass_lock;
};
现在这将保持aPass
完全安全,没有任何错误,永远不会碰它,是的! 然而,看看所有混乱,并想象访问它时的混乱。 毛。
另一种方法是让它们都可访问,并确保在使用aPass之前锁定互斥锁。
pthread_mutex_lock(aPass_lock);
do something with aPass
pthread_mutex_unlock(aPass_lock);
但是,如果有一个新人参与该项目,如果你忘记一次锁定该怎么办呢。 我不喜欢调试线程问题他们很难。
是否有一个很好的方法(使用pthreads,因为我必须使用QNX,它几乎没有提升支持)要锁定单个变量而不需要大类,那么只需创建一个互斥锁就可以更安全吗?
std::atomic<double> aPass;
只要你有C ++ 11。
为了详细说明我的解决方案,它将是这样的。
template <typename ThreadSafeDataType>
class ThreadSafeData{
//....
private:
ThreadSafeDataType data;
mutex mut;
};
class apass:public ThreadSafeData<int>
此外,为了使其独特,最好使所有操作员和成员保持静态。 为此,您需要使用CRTP即
template <typename ThreadSafeDataType,class DerivedDataClass>
class ThreadSafeData{
//....
};
class apass:public ThreadSafeData<int,apass>
您可以轻松地创建自己的类来锁定构造上的互斥锁,并在销毁时将其解锁。 这样,无论发生什么,即使抛出异常,或者采取任何路径,都会在返回时释放互斥锁。
class MutexGuard
{
MutexType & m_Mutex;
public:
inline MutexGuard(MutexType & mutex)
: m_Mutex(mutex)
{
m_Mutex.lock();
};
inline ~MutexGuard()
{
m_Mutex.unlock();
};
}
class TestClass
{
MutexType m_Mutex;
double m_SharedVar;
public:
TestClass()
: m_SharedVar(4.0)
{ }
static void Function1()
{
MutexGuard scopedLock(m_Mutex); //lock the mutex
m_SharedVar+= 2345;
//mutex automatically unlocked
}
static void Function2()
{
MutexGuard scopedLock(m_Mutex); //lock the mutex
m_SharedVar*= 234;
throw std::runtime_error("Mutex automatically unlocked");
}
}
变量m_SharedVar确保了Function1()
和Function2()
之间的互斥,并且在返回时将始终解锁。
boost已经构建了类型来完成此任务:boost :: scoped_locked,boost :: lock_guard。
您可以创建一个类,作为变量的通用包装器,同步对它的访问。
为作业添加运算符重载,您就完成了。
考虑使用RAII idiom
,下面的代码只是想法,它没有经过测试:
template<typename T, typename U>
struct APassHelper : boost::noncoypable
{
APassHelper(T&v) : v_(v) {
pthread_mutex_lock(mutex_);
}
~APassHelper() {
pthread_mutex_unlock(mutex_);
}
UpdateAPass(T t){
v_ = t;
}
private:
T& v_;
U& mutex_;
};
double aPass;
int baPass_lock;
APassHelper<aPass,aPass_lock) temp;
temp.UpdateAPass(10);
您可以使用运算符而不是get / set来修改aPass类:
class aPass {
public:
aPass() {
pthread_mutex_init(&aPass_lock, NULL);
aPass_ = 0;
}
operator double () const {
double setMe;
pthread_mutex_lock(aPass_lock);
setMe = aPass_;
pthread_mutex_unlock(aPass_lock);
return setMe;
}
aPass& operator = (double setThis) {
pthread_mutex_lock(aPass_lock);
aPass_ = setThis;
pthread_mutex_unlock(aPass_lock);
return *this;
}
private:
double aPass_;
pthread_mutex_t aPass_lock;
};
用法:
aPass a;
a = 0.5;
double b = a;
这当然可以模仿支持其他类型。 但请注意,在这种情况下,互斥量过大。 通常,在保护小数据类型的负载和存储时,内存屏障就足够了。 如果可能,您应该使用C ++ 11 std::atomic<double>
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.