简体   繁体   English

一旦作家完成了读者作家锁的写作,读者就不会醒来

[英]Reader doesn't wake up once writer finishes writting in Reader writer lock

class ReadLock
{
private:
    std::mutex readWriteMutex;
    std::mutex conditionmtx;
    std::condition_variable cv;
    int readings = 0;
    int writings = 0;
    int writers = 0;
public:
    void AquireReadLock()
    {
        readWriteMutex.lock();
        if (writers)
        {
            std::unique_lock<std::mutex> lck(conditionmtx);
            cv.wait(lck);
        }
        while (writings)
        {
            std::unique_lock<std::mutex> lck(conditionmtx);
            cv.wait(lck);
        }
        readings++;
        readWriteMutex.unlock();
    }
    void ReleaseReadLock()
    {
        readWriteMutex.lock();
        //std::unique_lock<std::mutex> lck(conditionmtx);
        cv.notify_all();
        readings--;
        readWriteMutex.unlock();
    }
    void  AquireWriteLock()
    {
        readWriteMutex.lock();
        writers++;
        while (readings || writings)
        {
            std::unique_lock<std::mutex> lck(conditionmtx);
            cv.wait(lck);
        }
        writings++;
        readWriteMutex.unlock();
    }
    void ReleaseWriteLock()
    {
        readWriteMutex.lock();
        writings--;
        writers--;;
        //std::unique_lock<std::mutex> lck(conditionmtx);
        cv.notify_all();
        readWriteMutex.unlock();
    }

};

ReadLock lock;
void WriteFunction(int id)
{
    std::cout << "thread " + std::to_string(id) + " asks for write " << '\n';
    lock.AquireWriteLock();
    std::cout << "thread " + std::to_string(id) + " writting" << '\n';
    std::this_thread::sleep_for(std::chrono::milliseconds(3500));
    std::cout << "thread " + std::to_string(id) + " finished writting" << '\n';
    lock.ReleaseWriteLock();
}
void ReadFunction(int id)
{
    if (id == 0)
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    std::cout << "thread " + std::to_string(id) + " asks for read" << '\n';
    lock.AquireReadLock();
    std::cout << "thread " + std::to_string(id) + " reading" << '\n';
    std::this_thread::sleep_for(std::chrono::milliseconds(2500));
    std::cout << "thread " + std::to_string(id) + " finished reading" << '\n';
    lock.ReleaseReadLock();
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::thread threads[3];

    for (int i = 0; i < 3; ++i)
    if (i % 2 == 0)
        threads[i] = std::thread(ReadFunction, i);
    else
        threads[i] = std::thread(WriteFunction, i);

    for (auto& th : threads) th.join();

}

i am trying to implement reader writer lock using condition variable and Mutex. 我正在尝试使用条件变量和Mutex来实现阅读器作家锁定。 Thread 2 writes first and thread 0 and thread 1 waits for thread 2 to finish writing but once thread 2 finished writing thread 1 and thread 0 are not waking up to read.can someone Help me out with this ? 线程2首先写入,线程0和线程1等待线程2完成写入,但是一旦线程2完成写入,线程1和线程0都没有醒来读取。有人可以帮我吗? i am new to c++ synchronisation 我是C ++同步的新手

This code generates a deadlock either on the writerRelease() or the readerRealease() debending which was fist aqcuired. 这段代码会在第一个被要求的writerRelease()readerRealease()上生成死锁。

How to find out ? 如何找出?

Multithread code is difficult to debug. 多线程代码很难调试。 I suggest you to add here some logging with to show when entering in an aquire/release function and when the mutex was locked. 我建议您在此处添加一些日志记录,以显示在输入获取/释放功能时以及互斥锁被锁定时的情况。

For example: 例如:

void ReleaseReadLock()
{
    cout <<this_thread::get_id()<< " will release ReadLock" << endl;
    readWriteMutex.lock();
    cout << this_thread::get_id() << " ...mutex locked" << endl;
    cv.notify_all();
    cout << this_thread::get_id() << " ...notified" << endl;
    readings--;
    readWriteMutex.unlock();
    cout << this_thread::get_id()<<" released ReadLock " << endl;
}

With such code, you will observe this scenario (or a slight variant of it): 使用这样的代码,您将观察到这种情况(或它的微小变化):

5204 thread 1 asks for write
3692 thread 2 asks for read
5204 will aquire WriteLock   ==> start write lock acquisition 
3692 will aquire ReadLock    ==> start read lock acquisition  
3692 ...mutex locked         ==> mutex was locked for read lock  acquisition
3692 aquired ReadLock        ==> mutex was unlocked : end read lock acquisition. 
5204 ...mutex locked         ==> mutext was locked for writelock 
3692 thread 2 reading        
5288 thread 0 asks for read
5288 will aquire ReadLock    ==> another read lock will wait for mutex
3692 thread 2 finished reading
3692 will release ReadLock   ==> Reader can't release lock because mutex is locked by writelock

What happens ? 怎么了 ?

The release lock was aquired succesfully. 成功获取了释放锁。 So readings is 1 . 所以readings是1。

To decrease this variable, releaseReadLock() has to terminate its job. 要减少此变量, releaseReadLock()必须终止其作业。 But it can't because the mutex it needs at the beginning of the function, is still held by the aquireWriteLock() . 但这不能,因为在函数开始时所需的互斥锁仍由aquireWriteLock() So it waits. 因此,它等待。

But the aquireWriteLock() is stuck in a loop, that will continue to loop as long as readings or writings. 但是aquireWriteLock()被卡在一个循环中,只要readings或书写,该循环就会继续循环。 It will release the mutex only of readings goes back to 0. 它将释放互斥锁,只有readings回到0。

In short, releaseReadLock() and acquireWriteLock() are both stuck, waiting for each other. 简而言之, releaseReadLock()acquireWriteLock()都被卡住,彼此等待。

How to solve it ? 怎么解决呢?

Well, deadlocks are a pretty nasty thing. 好吧,僵局是一件很讨厌的事情。

One thing that helsp is to always perform locking on several objects in the same order. 困扰的一件事就是总是以相同的顺序对多个对象执行锁定。 Then one thred might eventiually fail the locking, but there'll be no "kiss of death". 这样一来,可能最终无法使锁定失败,但是不会有“死亡之吻”。

More specifically, looking at your code, I have the impression that your readWriteMutex is there mostly to protect against race conditions on your 3 counters. 更具体地说,在查看您的代码时,我的印象是您的readWriteMutex在那里主要是为了防止3个计数器出现竞争状况。 I'd suggest to get rid of this mutex and use atomic variables instead. 我建议摆脱此互斥锁,而改用原子变量。

Then in ReleaseReadLock() you should decrement the number of readers before notifying the waiting threads. 然后,在ReleaseReadLock()中,应在通知等待线程之前减少读取器的数量。 With these two measures, I could run several times without deadlock (which do not prove that it's perfect, but at least the most obvious cases are avoided. up to you to analyse/verify in detail). 通过这两种措施,我可以无死锁地运行几次(这并不能证明它是完美的,但是至少可以避免最明显的情况。由您来详细分析/验证)。

class ReadLock
{
private:
    std::mutex conditionmtx;
    std::condition_variable cv;
    atomic<int> readings = 0;   // atomics don't need mutex for being updated
    atomic<int> writings = 0;
    atomic<int> writers = 0;
public:
    void AquireReadLock()
    {
        cout << this_thread::get_id() << " will aquire ReadLock" << endl;
        if(writers) {
            std::unique_lock<std::mutex> lck(conditionmtx);
            cv.wait(lck);
        }
        while(writings) {
            std::unique_lock<std::mutex> lck(conditionmtx);
            cv.wait(lck);
        }
        readings++;
        cout << this_thread::get_id() << " aquired ReadLock" << endl;
    }
    void ReleaseReadLock()
    {
        cout <<this_thread::get_id()<< " will release ReadLock" << endl;
        readings--;
        cv.notify_all();
        cout << this_thread::get_id()<<" released ReadLock " << endl;
    }
    void  AquireWriteLock()
    {
        cout << this_thread::get_id() << " will aquire WriteLock" << endl;
        writers++;
        while(readings || writings) {
            std::unique_lock<std::mutex> lck(conditionmtx);
            cv.wait(lck);
        }
        writings++;
        cout << this_thread::get_id() << " aquired  WriteLock" << endl;
    }
    void ReleaseWriteLock()
    {
        cout << this_thread::get_id() << " will release WriteLock" << endl;
        writings--;
        writers--;;
        cv.notify_all();
        cout << this_thread::get_id() << " ...notified" << endl;
        cout << this_thread::get_id() << " released WriteLock" << endl;
    }
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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