简体   繁体   中英

Strange C++11 mutex behavior

I am trying to implement a counting semaphore using binary ones.

The code logic is well known and it has been tested before using Qt's QMutex class and it is known to work properly (tested with a producer-consumer multithreaded driver). I have tried manual control of the l mutex (to lock the class), lock_guard and unique_lock objects, to no effect.

A thread calling acquire() seems to get blocked on the block mutex (not consistently though), even when other threads call block.unlock() . The only way to make the code work is to replace the block.lock() line with while(block.try_lock()); Then everything works properly.

The code works if I replace the block mutex with a condition variable.

#include <mutex>

class semaphore
{
private:
    int value=0;
    std::mutex l, block;

public:
    semaphore(int i=0);
    semaphore(const semaphore&)=delete;
    semaphore(const semaphore&&)=delete;
    semaphore & operator=(const semaphore&)=delete;
    semaphore & operator=(const semaphore&&)=delete;
    void acquire();
    void acquire(unsigned int i);
    void release(unsigned int i=1);
    int available();
    bool try_acquire(unsigned int i=1);
};

//---------------------------------
semaphore::semaphore(int i)
{
    value=i;
    block.lock(); // make sure that any thread trying to lock will block
}
//---------------------------------
void semaphore::acquire()
{
    l.lock();
    value--;  

    if(value <0)
     {
        l.unlock();  // release the semaphore while waiting
        while( block.try_lock());  // LINES IN QUESTION 
        //block.lock();            // LINES IN QUESTION 
     }
     else
         l.unlock();
}
//---------------------------------
void semaphore::acquire(unsigned int i)
{ 
    while(i--)
        this->acquire();
}
//---------------------------------
void semaphore::release(unsigned int i)
{
  l.lock();
  while(i--)
    {
      value++;  
      if(value <=0)
        block.unlock();
    }
  l.unlock();  
}
//---------------------------------
int semaphore::available()
{
    std::unique_lock<std::mutex> guard(l);
    return value;
}
//---------------------------------
bool semaphore::try_acquire(unsigned int i)
{
    l.lock();
    bool res=false;
    if(value>=(int)i)
    {
        value-=i;
        res=true;
    }
    l.unlock();
    return res;
}

Undefined behaviors:

  • try_lock cannot be called by a thread that already owns the mutex, otherwise, the behavior is undefined. Line while( block.try_lock()); is incorrect.
  • unlock can only be called by a thread that owns the mutex, otherwise, the behavior is undefined.

The #2 is also wrong for QtMutex . If the code was "working", it did so by an accident.

You need a proper semaphore implementation, something that is partially covered here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM