Domanda

I would like to ask whether it is OK to use 1 condition variable associated with 2 mutex, for 2 kind of data update.

Basically, I have thread1 and thread2. Thread1 could wait for 2 kinds of data update, so it got 2 mutex, one for each. (I know I could use one mutex for all these data, but this is not the point of this question, right?) And I certainly does not want it be waiting on data1 while data2 is already available, so it only got 1 condition variable. And thread2 would provide both data1 and data2. The problem is that in thread2, I don't know whether thread1 is now waiting for data1 or data2 or not waiting at all.

Pseudo code would look like below:

global data:
    cond_var
    mutex1
    data1
    mutex2
    data2

thread1:
    while true
        lock1.lock(mutex1)
        if data1 is not available
        cond_var.wait(lock1)
        if data1 is available
            process data1
        lock1.unlock()

        lock2.lock(mutex2)
        if data2 is not available
            cond_var.wait(lock2)
        if data2 is available
            process data2
        lock2.unlock()

thread2:
    func1:
        lock1.lock(mutex1)
        update data1
        cond_var.notify_all()
        lock1.unlock()

    func2:
        lock2.lock(mutex2)
        update data2
        cond_var.notify_all()
        lock2.unlock()

Outside world will call func1 or func2 to update the data. When func1 or func2 is called, it will signal cond_var, whether it is in lock1 or lock2. The wait of cond_var is not surrounded by while, so if cond_var is waked on lock1 but data2 is available, thread1 will go on to process data2.

The actual implementation is via boost::thread, and since the platform for my test is Linux, boost::thread should be implemented via pthread.

In almost all tutorial and documents I read about condition variable, it is associated with only 1 mutex. So I am curious to know whether the above program is OK to use, or is fundamentally flawed.

È stato utile?

Soluzione

This is OK.

Boost.Thread implements the C++11 thread library (it had some differences until v1.50 but is now very close) and that thread library is conceptually close to the Pthread model, but using a different API, so we can look at those specs for the answer.

The rule in the C++11 standard is:

Requires: lock.owns_lock() is true and lock.mutex() is locked by the calling thread, and either
— no other thread is waiting on this condition_variable object or
lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_for, or wait_until) threads.

In your case the mutex is correctly locked and there is only ever one thread waiting on the condvar, so the condition is met.

The rule in POSIX is equivalent, but worded differently:

The effect of using more than one mutex for concurrent pthread_cond_timedwait() or pthread_cond_wait() operations on the same condition variable is undefined; that is, a condition variable becomes bound to a unique mutex when a thread waits on the condition variable, and this (dynamic) binding shall end when the wait returns.

Again, you have no concurrent wait operations.

In general it's OK to use a condvar with different mutexes as long as you never wait using different mutexes at the same time. When waiting on a condvar you associate it with a mutex and a predicate (the "condition") so as POSIX describes, the condvar and mutex are "bound" together and any a condvar must not be bound to more than one mutex at a time. The reason is that when a condvar wakes up from a wait it must reacquire the mutex it is associated with, if different threads have used it with different mutexes it might re-lock the wrong mutex in the wrong thread, causing chaos. A thread would wake up thinking it has re-locked the mutex it waited with, but it's actually got a lock on a different mutex, maybe one it doesn't have any reference to and so can never unlock. Deadlock and/or undefined behaviour and lions and tigers and bears, oh my.

In your case there are no concurrent waits, so no problem. If you had more than one thread waiting you would need to ensure that the same mutex is used by both threads for any given wait ... which would be difficult as you'd need some additional synchronisation, so it would be simpler to just use two condvars.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top