2
votes

In my program I have a single mutex and two threads. One of these threads acquires the lock very often. The other thread tries to acquire but has to wait forever.

Could it be that the lock is acquired so quick after releasing it that the other thread does not get a chance? Does a mutex always give everyone a chance? If not, what would be a good solution?(some kind of FIFO lock?)

I am using std::mutex and std::lock_guard

Question expansion seccpur pointed out that an std::condition_variable would be a solution to this problem. How does this scale with three threads? Does std::condition_variable assure every thread gets a turn? Assuming you use notify_one().

3
Conditional variable should work perfectly in your caseseccpur
Are you using std::mutex and std::lock_guard? std::condition_variable? It would be nice to see some code.davepmiller
I'm using std::mutex and std::lock_guard.Aart Stuurman
@seccpur How would condition_variable scale with three threads? Could it be that two threads together starve out a third thread?Aart Stuurman
@AartStuurman: Use notify_all to broadcast to multiple threadsseccpur

3 Answers

3
votes

An std::mutex does not guarantee giving everyone an equal chance. So it is possible that one thread starves another. The first thing you can try is to insert std::this_thread::yield() and see if it helps. If this does not help, then your code must have logic errors. Post some portion of the code and we can help you diagnose further.

2
votes

Using seccpur's hints I came up with the following solution to prevent starving out a single thread.

#include <condition_variable>
#include <mutex>
#include <atomic>

class NoStarveLock
{
    std::condition_variable condition;
    std::atomic_flag flag = ATOMIC_FLAG_INIT;
    std::mutex conditionLock;
public:
    void lock()
    {
        std::unique_lock<std::mutex> lck(conditionLock);
        while (flag.test_and_set()) // multiple threads can wake up at the same time, so use a set+test
        {
            condition.wait(lck); // wait for a wakeup
        }
    }

    void unlock()
    {
        std::unique_lock<std::mutex> lck(conditionLock);
        flag.clear();
        condition.notify_all();
    }
};
0
votes

An example using std::mutex, std::lock_guard, std::unique_lock(reader) and std::condition_variable. _mutexLockCondition, _mutexObject, and _dataContainer are shared between ReaderClass and WriterClass.

Tried to be a bit vague on the actual data container, so getDataContainer() and addDataToContainer() are up to you.

std::shared_ptr< Data > ReaderClass::read()
{
    std::unique_lock< std::mutex > lock( _mutexObject );

    // handle spurious wakeup from waitForMessageNotification
    while( _dataContainer.empty() )
    {
        if( waitForMessageNotification( lock ) )
        {
            // timeout occurred, return nullptr to prevent blocking
            return nullptr;
        }
    }

    return getDataFromContainer();
}

bool ReaderClass::waitForNotification( unique_lock< mutex > & lock )
{
    //_mutexLockCondition is a std::condition_variable
    return _mutexLockCondition.wait_for( lock, std::chrono::milliseconds( 100 ) )
                                            == std::cv_status::timeout;
}


void WriterClass::write( std::shared_ptr< Data > dataPtr )
{
    std::lock_guard< mutex > lock( _mutexObject );

    addDataToContainer( dataPtr );

    _mutexLockCondition.notify_one();
}