9

我理解线程安全的概念。在尝试保护单个变量时,我正在寻找简化线程安全的建议。

假设我有一个变量:

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)锁定单个变量而不需要一个大类,这比创建一个互斥锁更安全吗?

4

6 回答 6

24
std::atomic<double> aPass;

前提是你有 C++11。

于 2012-11-09T12:42:54.957 回答
3

为了详细说明我的解决方案,它会是这样的。

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>
于 2012-11-09T01:29:49.527 回答
2

您可以轻松创建自己的类,在构造时锁定互斥体,并在破坏时解锁它。这样,无论发生什么情况,互斥体都会在返回时被释放,即使抛出异常或采用任何路径。

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

}

Function1()变量 m_SharedVar 确保和之间的互斥Function2(),并且总是在返回时解锁。

boost 具有内置类型来完成此操作:boost::scoped_locked、boost::lock_guard。

于 2012-11-09T01:32:05.227 回答
1

您可以创建一个类,该类充当您的变量的通用包装器,以同步对它的访问。

为赋值添加运算符重载,您就完成了。

于 2012-11-09T01:25:15.720 回答
1

考虑使用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);
于 2012-11-09T01:31:04.137 回答
1

您可以使用运算符而不是 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>

于 2012-11-09T02:39:50.107 回答