0

I've got a common problem (that was posted here in several variations) of a process running multiple threads that each may read or write to a group of common variables (for the simplicity - say it's single variable).

Each variable should be protected against parallel write, and allow parallel read.

I've seen several solutions, and decided to focus on the following one that is using guards (mentioned in this link)

However, I Couldn't figure out the following principles:

  1. MutexGuard class : shouldn't it be implemented as singleton. otherwise, each thread will create new mutex, other than waiting on a single common mutex.

  2. if the class MutexGuard is not singleTon, so at least the m_Mutex should be static so it will be shared among all instances of this class.

  3. why are function1 and function2 defined as static. this way it can be called without the context of an instance (class is just namespace), and the value of m_SharedVar might be missing.

  4. is there any easy way to modify the implementation for multi-readers / single writer lock ? should i only change the mutex type in this case ?

    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");
       }
    }
    
Community
  • 1
  • 1
Zohar81
  • 4,554
  • 5
  • 29
  • 82
  • *Each variable should be protected against parallel write, and allow parallel read.* — be careful here, as long as some thread can write, you have to synchronize concurrent reads to prevent data race. – Daniel Langr Apr 07 '16 at 06:26
  • 1
    Your code's inconsistent: if `Function1` and -`2` are made `static`, you can't access non-`static` member variables like `m_SharedVar`. They shouldn't be `static` unless `m_SharedVar` is also `static`. Basically, start by thinking about whether your data should be `static` or not (we can't tell you), then make the `mutex` protecting it the same. For concurrent readers, switch to a read write lock (that will involve a new API and changes to your code, to say whether read or write access is needed); see [here](http://stackoverflow.com/questions/16774469/a-rw-lock-for-c11-threads). – Tony Delroy Apr 07 '16 at 06:28
  • @TonyD, thanks for the reply, That's exactly what i thought. i copied this solution form the link mention in my question, and i figured out there's an issue with the static function definition. However, i'm still not sure if i need to make the class singleton, or not. – Zohar81 Apr 07 '16 at 07:09
  • @Zohar81: a good use for a singleton is very much the exception case. If you have something like a log file that the entire application must share, then a singleton may make sense, and the mutex itself may owned by the singleton object, but the `MutexGuard` objects themselves will never be or involve singletons in their implementation: they just lock/unlock the mutex/lock they're aimed at, and shouldn't care how access to and the number of such locks is coordinated within the application. – Tony Delroy Apr 07 '16 at 07:13

1 Answers1

2

I think the problem here is the TestClass. As rightly pointed by @TonyD, the implementation of MutexGuard shouldn't be bound to singleton unless it is explicitly necessary. The intention of MutexGuard is to perform lock() & unlock() of the given mutex in automatic way, for the caller. So the caller of MutexGuard doesn't have to explicitly lock() or unlock().

Now it becomes the responsibility of TestClass to guard the mutex from multiple access. Typically all the worker threads that uses the functions of the TestClass will be automatically guarded if each function is locking the mutex that is shared with the instance of the class. Again it is implementation specific. There can be one static mutex for all the instances or there can multiple mutexes aimed for different shared resources etc.

Now coming to your question of

is there any easy way to modify the implementation for multi-readers / single writer lock ? should i only change the mutex type in this case ?

You will have to understand that, multiple readers / single write is a myth. It doesn't guarantee parallelism. It only means that, the locking / unlocking gets quicker based on need basis. i.e Unless some is writing the date, multiple readers can read the data ( assuming that the value is not changed ). In such case, you might want to implement something known as reader-writer lock.

My professor always term mutex locks as fat-locks. It doesn't really discriminate between a read / write and always has constant locking time irrespective of the work that you intend to do on the shared resource.

For more info, please refer to this stackoverflow post. Or you can also use ready made BOOST reader-writer locks.

Community
  • 1
  • 1
kspviswa
  • 637
  • 3
  • 13
  • Nice answer. One nit-pick: *"multiple readers / single write[r] is a myth"* - the term is usually meant to communicate that multiple readers ***or*** a single writer can be active at any point in time - a more nuanced term/description for what you prefer to call reader-writer locks. Perhaps when written with "/" as you have it could be *misunderstood* to mean a lock allowing multiple readers ***and*** a single writer concurrently - can understand you debunking that misconception. – Tony Delroy Apr 08 '16 at 01:38