【问题标题】:optimize the implementation of read/write locks优化读/写锁的实现
【发布时间】:2015-05-05 04:35:48
【问题描述】:

我目前正在使用 boost 库实现读/写锁,而不使用 shared_lock 和 unique_lock。我已经阅读了一些相关的问题(例如,How would a readers/writer lock be implemented in C++11?),但我仍然想优化实现。

这是我的代码:

enum LockType { NO_LOCK, READ_LOCK, WRITE_LOCK, INC_LOCK };
boost::mutex mutex_;
boost::condition condition_;
LockType lock_;
size_t owner_count_;

void AcquireReadLock() {
    mutex_.lock();
    while (lock_ != NO_LOCK && lock_ != READ_LOCK){
        condition_.wait(mutex_);
    }
    // if there is no lock, then acquire read lock.
    if (lock_ == NO_LOCK) {
        lock_ = READ_LOCK;
        ++owner_count_;
        mutex_.unlock();
        return;
    }
    else {
        // if there is read lock, then still acquire read lock.
        assert(lock_ == READ_LOCK);
        ++owner_count_;
        mutex_.unlock();
        return;
    }
}

void AcquireWriteLock() {
    mutex_.lock();
    while (lock_ != NO_LOCK){
        condition_.wait(mutex_);
    }
    // if there is no lock, then acquire write lock.
    assert(lock_ == NO_LOCK);
    lock_ = WRITE_LOCK;
    mutex_.unlock();
    return;
}

void ReleaseReadLock() {
    mutex_.lock();
    --owner_count_;
    if (owner_count_ == 0) {
        lock_ = NO_LOCK;
    }
    mutex_.unlock();
    // is it correct to use notify_all?
    condition_.notify_all();
}

void ReleaseWriteLock() {
    mutex_.lock();
    lock_ = NO_LOCK;
    mutex_.unlock();
    // is it correct to use notify_all?
    condition_.notify_all();
}

问题是:

  1. 释放锁时是否应该使用 notify_all?根据文档,一旦一个线程得到通知,它就会重新获得锁。如果使用 notify_all,则多个线程可以重新获取同一个锁。那时会发生什么?以及线程是否会在检查条件之前获取锁(即 lock_!=NO_LOCK && lock_!=READ_LOCK)?

  2. 如何优化程序?显然,当释放读锁时,我们只需要通知试图获取写锁的线程,因为读永远不会阻塞读。那么如何实现这个想法呢?

提前感谢您的帮助!

【问题讨论】:

    标签: c++ multithreading boost


    【解决方案1】:
    1. 释放锁时是否应该使用notify_all?根据文档,一旦一个线程得到通知,它就会重新获得锁。如果使用 notify_all,则多个线程可以重新获取同一个锁。那时会发生什么?以及线程是否会在检查条件之前获取锁(即 lock_!=NO_LOCK && lock_!=READ_LOCK)?

    是的,您应该在释放锁时使用 notify_all。所有等待互斥锁的条件_都会被一一唤醒,因为它们必须首先锁定互斥锁(这是在条件_等待操作中完成的)。

    1. 如何优化程序?显然,当释放读锁时,我们只需要通知试图获取写锁的线程,因为读永远不会阻塞读。那么如何实现这个想法呢?

    必须通知所有等待mutex_的线程,因为一些写线程可能等待读锁被释放。

    希望对你有帮助!

    【讨论】:

    • hi yanchong,我认为您的回答与@Tsyvarev 的回答相矛盾,因为他认为 notify 就足够了。我们只需要通知一个线程,并且被通知的线程会在进入临界区之前检查条件并获取锁,这似乎是合理的。对吗?
    • @Frank,在你的ReleaseReadLock() 中,当调用notify 时,你的_mutex 被解锁,所以可能有另一个写线程只是AcquireWriteLock 成功,之后一些读线程被阻塞在AcquireReadLock,因此,在你的 notify 被调用之前,有一些读写线程都在等待 _mutex,所以 notify_all 总是有帮助的。
    【解决方案2】:

    如果使用 notify_all,那么多个线程可以重新获取同一个锁。

    没有。每个被通知的线程都会竞争获取锁,就像mutex_.lock()一样。即使使用 notify_all,最多只有一个线程可以同时执行临界区的代码。所以,使用 notify_all 是完全正确的。

    1. 如何优化程序?显然,当释放读锁时,我们只需要通知试图获取写锁的线程,因为读永远不会阻塞读。那么如何实现这个想法呢?

    由于读取从不阻塞读取,因此没有读取线程可以wait() 其他读取线程。所以即使你当前的代码ReleaseReadLock()也只能通知写线程。

    因此,在ReleaseReadLock() 中,您可以安全地使用notify() 而不是notify_all:没有理由唤醒所有写入线程,因为只有其中一个可以获取您的读写锁。

    至于其他优化,您最好修复伪影,在此答案中列出:https://stackoverflow.com/a/12657243/3440745

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-05
      • 2015-01-17
      • 1970-01-01
      • 2018-08-28
      • 1970-01-01
      • 1970-01-01
      • 2015-10-15
      相关资源
      最近更新 更多