Fantastic Mr Fox
Fantastic Mr Fox

Reputation: 33944

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! 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.

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?

Upvotes: 12

Views: 22440

Answers (6)

Pete Becker
Pete Becker

Reputation: 76458

std::atomic<double> aPass;

provided you have C++11.

Upvotes: 25

rasmus
rasmus

Reputation: 3226

You can modify your aPass class by using operators instead of get/set:

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>.

Upvotes: 1

Karthik T
Karthik T

Reputation: 31962

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 i.e

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

Upvotes: 3

J-Mik
J-Mik

Reputation: 896

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.

boost has build in types to accomplish this: boost::scoped_locked, boost::lock_guard.

Upvotes: 2

billz
billz

Reputation: 45450

Consider use RAII idiom, below code is just the idea, it's not tested:

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

Upvotes: 1

thedayofcondor
thedayofcondor

Reputation: 3876

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.

Upvotes: 1

Related Questions