I've found that often in my code I have a need for primitive variables protected with synchronization objects such as a boost::mutex, and I was wondering if the boost library provides a template similar to this one I'm considering, based on the C# property methods get/set:

template<typename var_type>
	class ThreadSafeVariable{
		boost::mutex mut;
		var_type value;
	public:
		var_type get(){
			boost::mutex::scoped_lock(mut);
			return value;
		}
		void set(var_type newValue){
			boost::mutex::scoped_lock(mut);
			value = newValue;
		}
	};

Recommended Answers

All 9 Replies

Well, primitives are generally thread-safe just by the virtue of value-semantics and their primitive nature (as in, any read/write operation on them is atomic, i.e. it's extremely unlikely that a thread switch would occur in the middle of the operation, which is really where mutexes are required).

For larger classes, the thread-safety should really be implemented as part of the particular class. boost::shared_ptr are lock-free and thread-safe, like a primitive type, and dereferencing the underlying pointer is as thread-safe as the class of the object it's pointing to.

If you do want to implement this template you posted, may I suggest that you use more natural semantics instead of this ugly get/set:

template<typename var_type>
class ThreadSafeVariable {
  mutable boost::mutex mut;
  var_type value;
public:
  operator var_type() const {
    boost::mutex::scoped_lock(mut);
    return value;
  }
  ThreadSafeVariable<var_type>& operator =(const var_type& newValue){
    boost::mutex::scoped_lock(mut);
    value = newValue;
    return *this;
  }
};

This should allow you to basically substitute an object of type double for an object of type ThreadSafeVariable<double> without changing anything on the user-side (all arithmetic operations and what-not will be unchanged by trigger an implicit, thread-safe local copy of value to be used). Obviously, this will have a lot of overhead because lock and unlock mutexes is an expensive operation, so you will almost always be better off locking a mutex externally to avoid any repetitive locking and unlocking. To solve this, you can also use a mechanism similar to the weak_ptr/shared_ptr relationship. With weak_ptr/shared_ptr, the weak_ptr can only lock a shared_ptr if the ref count is above zero, then the lock() function creates a shared_ptr that is usually local to a small scope and is released when it goes out-of-scope. I recommend a similar mechanism for your ThreadSafeVariable. For example:

template <typename T>
class locked_var {
  private:
    boost::unique_lock l;
    T* ptr;
  
    template <typename Lockable>
    locked_var(T* aPtr, Lockable m) : l(m), ptr(aPtr) { };

  public:
    friend class thread_safe_var<T>;

    typedef const locked_var<T>& type;
    
    operator T&() const { return *ptr; };
    const locked_var<T>& operator=(const T& val) const { *ptr = val; return *this; };
};

template<typename T>
class thread_safe_var {
  mutable boost::mutex mut;
  T value;
public:
  locked_var<T> lock() { return locked_var<T>(&value,mut); };
};

int main() {
  thread_safe_var<double> my_var;

  { //in some thread function's scope, let's say:
    locked_var<double>::type v = my_var.lock();

    double a = v + 3.0;
 
    v = a;

  }; // at this point, the lock inside v will be released.
};

The above might not work as is because there are a few weird things about it (including the binding of a temporary rvalue to a const reference), but you get the idea. This would basically make the lock happen only once which is much more efficient and you don't suffer the overhead of copying during the operations with variable.

Excellent ideas, and I learned a new keyword too!

Mutable

It always occurred to me I couldn't use a const member function to get a copy of my variable when protected by a mutex...

Perhaps my understanding of the C++ primitive types is flawed, I'm using this for the sensitivity values in a class to simulate mouse movement, it will be read-from quite often and I need to be able to change the sensitivity on-the-fly (asynchronously). Do I need to protect the variable?

>>Do I need to protect the variable?
No. I'm guessing your sensitivity values are of a primitive type like double or int. All read-only operations are thread-safe, period (that is also valid for a non-primitive POD types). As for writing, storing a double or int value is essentially a single operation, I mean, at one clock cycle the value stored will be the previous value, and at the next clock cycle the value will have changed. Where can there be a thread-switch between the two that would cause the reading of an invalid value? That's why primitives are considered "lock-free thread-safe", because they are thread-safe without requiring a mutex lock (as far as I know, only primitives are like that, or near-primitives like smart-pointers).

The only case where it would matter is if you have a function that reads the value multiple times and expects it to be the same every time. In that case, just read it once into a local variable at the start of the function (or loop).

Mutex locking in multi-threaded software is only really needed when you have some data structure that has to go in a momentarily invalid state and might remain so for a little while. For example, resizing an array would usually involve resetting the "size" variable, then allocating a new array, copying the content, deleting the old array, and resetting pointer to its new location. The problem with this is that as soon as the size variable is changed and until the pointer has the new location, the container is no longer in a valid state and any operations to try and read/write its elements would be dangerous, this requires the locking of a mutex where basically "mutex free = valid state". With primitive types and other simple POD types, there just isn't any operation that can put the variable in an invalid state for more than the time between two clock ticks of the CPU.

If you have several primitives that are all required to be "synchronized", as in, if you just reset half of the values but not the other half before a thread-switch occurs, then the thread that reads them would be corrupted by the mismatching first and last half of the sequence, then you should probably have a lock on the entire read and write operations. But, remember, locking a mutex is a very expensive operation (because you are querying the kernel for a system-wide operation), you don't want to do this more often than you need.

PS: You are welcomed to use this little thread_safe_var code I posted, if you really find use for it. Of course, courtesy dictates the acknowledgement in whatever library it might end up. Frankly, I'm not even totally sure it works and I haven't tested.

I have a question about threading, with Visual Studio 2010 if this global bool isn't volatile, the program functions incorrectly. Do you think the compiler is applying some sort of optimization that causes this, or is it because of another reason?

#define NOMINMAX
#include <Windows.h>
#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>

volatile bool stopThread = false;//<-- if you make this non-volatile, it doesn't work.

void work(){
	while(!stopThread){
		//...
		//Sleep(1000);
	}
}

int main()
{
	using namespace boost;
	shared_ptr<thread> inc;
	inc = shared_ptr<thread>(new thread(work));
	Sleep(1000);
	::stopThread = true;
	if(inc != nullptr){
		if(inc->joinable())
			inc->join();
	}
	std::cout << "Thread is joined." << std::endl;
	std::cin.get();
}

This is expected. That is exactly why the volatile keyword exists.

Hmm, I was told incorrect information a while back when I was reading about the "volatile" keyword. Thank you for clarifying, as always you are extremely helpful. The poster/website/blogger or whomever it may have been claimed it was used for inter-process communication and served no purpose in my code, and I had written an example very similar to that one.

Interestingly the general consensus of most search results about multi-threading and primitive types is that they all require synchronization. Mutexes are mainly suggested. The reason I think I would have to use a mutex is because of the problem with reading from and writing to a variable at the same time, is this guaranteed to not happen on an x86 machine running Windows 7?

This actually depends on the architecture of the processor. For x86 and x86_64, all primitive types of 64bits or less guarantee that read/write operations are atomic (meaning they cannot be interrupted half-way by a context switch). At least, it is a pretty safe assumption. You can also use CAS (Compare-and-swap) to ensure integrity. But, frankly, I think locks are overkill for primitive types. You can always use platform-specific atomicity functions and a CAS, and you should be pretty safe.

As for this little thread_safe_var, here is the working version I cooked up:

#include <iostream>
#include <boost/thread/thread.hpp>
#include <string>

template<typename T>
class lockable_var {
  mutable boost::mutex mut;
  T value;

  class const_lock_impl; //forward-decl
  class lock_impl {
    private:
      mutable boost::unique_lock<boost::mutex> l;
      T* ptr;
      lock_impl(const lock_impl& rhs) : l(), ptr(rhs.ptr) { l.swap(rhs.l); };
      lockable_var<T>::lock_impl& operator=(const lockable_var<T>::lock_impl&); //non-assignable. 
    public:
      friend class lockable_var<T>;
      friend class const_lock_impl;
      explicit lock_impl(lockable_var<T>& tsv) : l(tsv.mut), ptr(&tsv.value) { };
      operator T&() const { return *ptr; };
      T& value() const { return *ptr; };
      const lockable_var<T>::lock_impl& operator=(const T& val) const { *ptr = val; return *this; };
  };
  class const_lock_impl {
    private:
      mutable boost::unique_lock<boost::mutex> l;
      const T* ptr;
      const_lock_impl(const const_lock_impl& rhs) : l(), ptr(rhs.ptr) { l.swap(rhs.l); };
      lockable_var<T>::const_lock_impl& operator=(const lockable_var<T>::const_lock_impl&); //non-assignable. 
    public:
      friend class lockable_var<T>;
      explicit const_lock_impl(const lockable_var<T>& tsv) : l(tsv.mut), ptr(&tsv.value) { };
      const_lock_impl(const lock_impl& rhs) : l(), ptr(rhs.ptr) { l.swap(rhs.l); };
      operator const T&() const { return *ptr; };
      const T& value() const { return *ptr; };
  };
public:
  typedef const lock_impl& lock_type;
  lock_impl lock() { return lock_impl(*this); };
  typedef const const_lock_impl& const_lock_type;
  const_lock_impl lock() const { return const_lock_impl(*this); };
  
};

lockable_var<std::string> my_str;

void printMyStr() {
  std::cout << my_str.lock().value() << std::endl;
};

int main() {
  {
    lockable_var<std::string>::lock_type s = my_str.lock();
    boost::thread t(printMyStr);
    
    s = "Hello ";
    std::cout << s.value();
    s = "World";
  };
  char c;
  std::cin >> c;
  return 0;
};
commented: +1 for excellence in daniweb-ness. +1

I've found that often in my code I have a need for primitive variables protected with synchronization objects such as a boost::mutex

If by primitive variables you mean variables of types like int, double, pointer and so on, using heavy weight synchronization (using mutexes and so on) as a general purpose technique is ill-advised (performance).

If you have a compiler that supports C++0x atomics, use that. The <atomic> header is closely coupled to the innards of the compiler; and ideally results in highly optimized machine code being generated. Something that is not otherwise representable by higher level C or C++ constructs. See N3092 for details http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2010/n3092.pdf

For example,

#include <iostream>
#include <string>
#include <atomic>

int main()
{
    std::atomic<int> cnt(0) ; // specialization for int
    std::cout << ++cnt << '\n' ; // atomic increment
    std::cout << ( cnt += 6 ) << '\n' ;
    // etc

    std::string str[10] { "", "", "", "", "", "hello ", "", "", "", "" };
    std::atomic< std::string* > pointer( str ) ; // atomic pointer
    pointer += 2 ; // atomic increment
    pointer[3] += "world" ; // note: only the pointer operation is atomic here
    std::cout << pointer[3] << '\n' ;
}

If not you could use Boost.Atomic (not part of Boost release) if you are on a platform that it supports. http://www.chaoticmind.net/~hcb/projects/boost.atomic/

Other options include just::thread http://www.stdthread.co.uk/doc/headers/atomic.html, Intel TBB http://threadingbuildingblocks.org/

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.