Настройки чтения-записи с использованием семафоров - PullRequest
1 голос
/ 21 декабря 2011

В настоящее время я работаю над правильной реализацией проблемы Reader-Writer (см. здесь ).

Я нашел это решение в доках Qt, гарантирующеесправедливое обращение с потоками Reader и Writer с использованием семафора и мьютекса.Основной код таков:

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_);
}

Это выглядит как очень элегантное решение.Кажется, это проще и намного эффективнее, чем поведение pthread_rwlock_*, описанное в этом SO ответе.

Мне было интересно, является ли приведенный ниже код корректной настройкой решения 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_);
}

1 Ответ

2 голосов
/ 21 декабря 2011

С вашим кодом довольно много проблем:

  1. Семафор используется только писателем и поэтому не имеет смысла.
  2. При блокировке для писателя вы используете мьютекс, а при разблокировке - нет.
  3. Считыватели сигнализируют об измененном состоянии, когда #readers становится равным нулю, и записывающее устройство ожидает сигнала переменной условия, но не проверяет условие.
  4. При блокировке для писателя, если #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 проще, но моему алгоритму не нужно заранее знать максимальное количество читателей.

...