简体   繁体   English

线程安全单个变量

[英]Thread Safety Of a single variable

I understand the concept of thread safety. 我理解线程安全的概念。 I am looking for advice to simplify thread safety when trying to protect a single variable. 我正在寻求在尝试保护单个变量时简化线程安全性的建议。

Say I have a variable: 说我有一个变量:

double aPass;

and I want to protect this variable, so i create a mutex: 我想保护这个变量,所以我创建了一个互斥锁:

pthread_mutex_t aPass_lock;

Now there are two good ways i can think of doing this but they both have annoying disadvantages. 现在有两种很好的方法可以让我想到这样做,但它们都有令人讨厌的缺点。 The first is to make a thread safe class to hold the variable: 第一个是创建一个线程安全类来保存变量:

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;
};

Now this will keep aPass totally safe, nothing can be mistaken and ever touch it, YAY! 现在这将保持aPass完全安全,没有任何错误,永远不会碰它,是的! however look at all that mess and imagine the confusion when accessing it. 然而,看看所有混乱,并想象访问它时的混乱。 gross. 毛。

The other way is to have them both accessible and to make sure you lock the mutex before you use aPass. 另一种方法是让它们都可访问,并确保在使用aPass之前锁定互斥锁。

pthread_mutex_lock(aPass_lock);
   do something with aPass
pthread_mutex_unlock(aPass_lock);

But what if someone new comes on the project, what if you forget one time to lock it. 但是,如果有一个新人参与该项目,如果你忘记一次锁定该怎么办呢。 I don't like debugging thread problems they are hard. 我不喜欢调试线程问题他们很难。

Is there a good way to (using pthreads because i have to use QNX which has little boost support) To lock single variables without needing a big class and that is safer then just creating a mutex lock to go with it? 是否有一个很好的方法(使用pthreads,因为我必须使用QNX,它几乎没有提升支持)要锁定单个变量而不需要大类,那么只需创建一个互斥锁就可以更安全吗?

std::atomic<double> aPass;

只要你有C ++ 11。

To elabourate on my solution, it would be something like this. 为了详细说明我的解决方案,它将是这样的。

template <typename ThreadSafeDataType>
class ThreadSafeData{
   //....
private:
   ThreadSafeDataType data;
   mutex mut;
};

class apass:public ThreadSafeData<int>

Additionally, to make it unique, it might be best to make all operators and members static. 此外,为了使其独特,最好使所有操作员和成员保持静态。 For this to work you need to use CRTP ie 为此,您需要使用CRTP

template <typename ThreadSafeDataType,class DerivedDataClass>
class ThreadSafeData{
//....
};
class apass:public ThreadSafeData<int,apass>

You can easily make your own class that locks the mutex on construction, and unlocks it on destruction. 您可以轻松地创建自己的类来锁定构造上的互斥锁,并在销毁时将其解锁。 This way, no matter what happens the mutex will be freed on return, even if an exception is thrown, or any path is taken. 这样,无论发生什么,即使抛出异常,或者采取任何路径,都会在返回时释放互斥锁。

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");
        }

}

The variable m_SharedVar is ensured mutual exclusion between Function1() and Function2() , and will always be unlocked on return. 变量m_SharedVar确保了Function1()Function2()之间的互斥,并且在返回时将始终解锁。

boost has build in types to accomplish this: boost::scoped_locked, boost::lock_guard. boost已经构建了类型来完成此任务:boost :: scoped_locked,boost :: lock_guard。

You can create a class which act as a generic wrapper around your variable synchronising the access to it. 您可以创建一个类,作为变量的通用包装器,同步对它的访问。

Add operator overloading for the assignment and you are done. 为作业添加运算符重载,您就完成了。

Consider use RAII idiom , below code is just the idea, it's not tested: 考虑使用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);

You can modify your aPass class by using operators instead of get/set: 您可以使用运算符而不是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;
};

Usage: 用法:

aPass a;
a = 0.5;
double b = a;

This could of course be templated to support other types. 这当然可以模仿支持其他类型。 Note however that a mutex is overkill in this case. 但请注意,在这种情况下,互斥量过大。 Generally, memory barriers are enough when protecting loads and stores of small data-types. 通常,在保护小数据类型的负载和存储时,内存屏障就足够了。 If possible you should use C++11 std::atomic<double> . 如果可能,您应该使用C ++ 11 std::atomic<double>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM