【问题标题】:Reader-Writer Preference Using Semaphores使用信号量的读写器偏好
【发布时间】:2011-12-21 22:37:10
【问题描述】:

我目前正在努力解决读写器问题(请参阅here)。

我在 Qt 扩展坞中找到了this 解决方案,该解决方案通过使用信号量和互斥量来保证读取器和写入器线程的公平处理。基本代码是这样的:

sem_t semaphore_;
pthread_mutex_t lock_;

void PalindromeDatabase::initializeLocks()
{
    sem_init(&semaphore_, 0, NumberOfReaders_);
    pthread_mutex_init(&lock_, nullptr);
}

void PalindromeDatabase::lockReaders()
{
    sem_wait(&semaphore_);
}

void PalindromeDatabase::unlockReaders()
{
    sem_post(&semaphore_);
}

void PalindromeDatabase::lockWriters()
{
    pthread_mutex_lock(&lock_);
    {
        for (int i = 0; i < NumberOfReaders_; ++i)
            sem_wait(&semaphore_);
    }
    pthread_mutex_unlock(&lock_);
}

void PalindromeDatabase::unlockWriters()
{
    for (int i = 0; i < NumberOfReaders_; ++i)
        sem_post(&semaphore_);
}

这似乎是一个非常优雅的解决方案。它似乎比this SO 答案中详述的pthread_rwlock_*behavior 更简单、更有效。

我想知道下面的这段代码是否是对 Qt 解决方案的正确调整,以偏爱 Reader 线程。

int readersActive_;
sem_t semaphore_;
pthread_mutex_t lock_;
pthread_mutex_t readLock_;
pthread_cond_t wait_;

void PalindromeDatabase::initializeLocks()
{
    sem_init(&semaphore_, 0, numberOfReaders_);
    pthread_mutex_init(&lock_, nullptr);
    pthread_mutex_init(&readLock_, nullptr);
    pthread_cond_init(&wait_, nullptr);
}

void PalindromeDatabase::lockReaders()
{
    pthread_mutex_lock(&lock_);
    {
        pthread_mutex_lock(&readLock_);
        sem_wait(&semaphore_);
        pthread_mutex_unlock(&readLock_);

        ++readersActive_;
    }
    pthread_mutex_unlock(&lock_);
}

void PalindromeDatabase::lockReaders()
{
    pthread_mutex_lock(&lock_);
    {
        pthread_mutex_lock(&readLock_);
        sem_wait(&semaphore_);
        pthread_mutex_unlock(&readLock_);

        ++readersActive_;
    }
    pthread_mutex_unlock(&lock_);
}

void PalindromeDatabase::unlockReaders()
{
    sem_post(&semaphore_);

    pthread_mutex_lock(&lock_);
    {
        --readersActive_;

        if (readersActive_ == 0)
            pthread_cond_signal(&wait_);
    }
    pthread_mutex_unlock(&lock_);
}

void PalindromeDatabase::lockWriters()
{
    pthread_mutex_lock(&lock_);
    {
        if (readersActive_ != 0)
        {
            do
            {
                pthread_cond_wait(&wait_, &lock_);
            } while (readersActive_ != 0);
        }

        pthread_mutex_lock(&readLock_);
        for (int i = 0; i < numberOfReaders_; ++i)
            sem_wait(&semaphore_);
        pthread_mutex_unlock(&readLock_);
    }
    pthread_mutex_unlock(&lock_);
}

void PalindromeDatabase::unlockWriters()
{
    for (int i = 0; i < numberOfReaders_; ++i)
        sem_post(&semaphore_);
}

【问题讨论】:

  • 不喜欢读取线程:这会导致写入线程永远不会写入,并且会使读取线程读取过期数据。
  • @stefaanv:是的,据我了解,大多数实现更喜欢编写器,因为它们很少见,而且写操作通常更重要。为了我自己的理解,我正在尝试实现所有三种变体(读取、写入、无偏好)。不过感谢您的提醒。

标签: c++ multithreading posix readerwriterlock


【解决方案1】:

你的代码有很多问题:

  1. 信号量仅供作者使用,因此没有意义。
  2. 为 writer 锁定时,您使用互斥锁,而解锁时您不使用。
  3. 当 #readers 变为零时,读取器会发出更改条件的信号,写入器会等待条件变量的信号,但不会检查条件。
  4. 为 writer 锁定时,如果 #readers 已经为零,则实际上不会锁定。

考虑到我说的很简单,锁定仍然很棘手,我想了想,我希望我能用这个伪代码破解它,专注于正确的顺序而不是正确的符号:

void lockReader()
{
  lock(rdmutex);  // make sure Reader and Writer can't interfere during locking
  lock(wrmutex);  // lock mutex so waitfor can unlock
  while (writer_)
    waitfor(wrcv, wrmutex);  // no active writers

  ++readers_; // at least 1 reader present
  unlock(wrmutex);
  unlock(rdmutex);
}

void unlockReader()
{
  lock(rdmutex);
  bool noReaders = (--readers_ == 0);
  unlock(rdmutex);
  if (noReaders) signal(rdcv); // signal when no more readers
}

void lockWriter()
{
  lock(WritersLock);  // only 1 writer allowed
  lock(rdmutex);  // lock mutex so waitfor can unlock and no interference by lockReader
  while (readers_ != 0)
    waitfor(rdcv, rdmutex);  // wait until no more readers
  lock(wrmutex);
  writer_ = true;  // a writer is busy
  unlock(wrmutex);
  unlock(rdmutex);
  // WritersLock is still locked
}

void unlockWriter()
{
  lock(wrmutex);
  writer_ = false;
  unlock(wrmutex);
  signal(wrcv);  // no more writer (until WritersLock is unlocked)

  unlock(WritersLock);
}

事实证明,Qt 实现更简单,但我的算法不需要提前知道最大读者数。

【讨论】:

  • 哎呀,我对 3 和 4 的错误。现在应该修复。你是什​​么意思,信号量仅供作者使用?读取器锁定信号量,一旦单个读取器使用了信号量,写入不能同时获取所有锁,必须执行 cond_wait。一旦最后一个读取器完成,它会唤醒写入器并且它可以获取所有信号量。这不正确吗?
  • @SoulBeaver:大约 1:我的错误,我忽略了 reader-lock(unlock) 的 sem 函数,大约 4:lockWriters 中发生了什么(readers_ == 0)?据我说,它在不调整信号量的情况下返回。大约 2:好的,您只对条件变量使用互斥锁。
  • @SoulBeaver:你当前的 UnlockReader 现在很奇怪,无法解锁
  • 我的错,我把lockWriters的更正放到了错误的函数中。现在应该修复和纠正它。
  • 实际上使用 2 个条件变量和一个写入器锁很容易选择阅读器:1. 锁定阅读器:等待没有写入器 (cv1),增加阅读器 2. 解锁阅读器:减少阅读器,当没有阅读器时发出信号(cv2) 3. lock writer:锁定 writer-mutex,等待没有读者 (cv2),设置 writer,保持 writer-mutex 锁定 4. unlock writer:重置 writer,信号没有 writer (cv1),解锁 writer-mutex跨度>
猜你喜欢
  • 2023-03-19
  • 2013-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多