Настройки чтения-записи с использованием семафоров
В настоящее время я работаю над правильной реализацией проблемы 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_*
поведение подробно описано в этом ответе.
Мне было интересно, является ли приведенный ниже код корректной настройкой решения 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 ответ
Есть несколько проблем с вашим кодом:
Семафор используется только писателем и как таковой он не имеет смысла.При блокировке для писателя вы используете мьютекс, а при разблокировке - нет.- Считыватели сигнализируют об измененном состоянии, когда #readers становится равным нулю, и записывающее устройство ожидает сигнала переменной условия, но не проверяет условие.
- При блокировке для писателя, если #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 проще, но моему алгоритму не нужно заранее знать максимальное количество читателей.