多线程编程之读写锁
Posted 拙园
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程编程之读写锁相关的知识,希望对你有一定的参考价值。
在《多线程编程之Linux环境下的多线程(二)》一文中提到了Linux环境下的多线程同步机制之一的读写锁。本文再详细写一下读写锁的概念和原理。
一、什么是读写锁
读写锁(也叫共享-独占锁)实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者。
如果读写锁当前没有读者,也没有写者,那么写者可以立刻获得读写锁,否则它必须“自旋”在那里,直到没有任何写者或读者。如果读写锁没有写者,那么读者可以立即获得该读写锁,否则读者必须“自旋”在那里,直到写者释放该读写锁。读写锁适合于对数据结构的读次数比写次数多很多的场合。
二、一种Linux环境下的实现方法
下面利用pthread.h提供的mutex和condition来实现一个读写锁:
#include <pthread.h>
struct rwlock {
pthread_mutex_t lock;
pthread_cond_t read, write;
unsigned readers, writers, read_waiters, write_waiters;
};
void reader_lock(struct rwlock *self) {
pthread_mutex_lock(&self->lock);
if (self->writers || self->write_waiters) {
self->read_waiters++;
do pthread_cond_wait(&self->read, &self->lock);
while (self->writers || self->write_waiters);
self->read_waiters--;
}
self->readers++;
pthread_mutex_unlock(&self->lock);
}
void reader_unlock(struct rwlock *self) {
pthread_mutex_lock(&self->lock);
self->readers--;
if (self->write_waiters)
pthread_cond_signal(&self->write);
pthread_mutex_unlock(&self->lock);
}
void writer_lock(struct rwlock *self) {
pthread_mutex_lock(&self->lock);
if (self->readers || self->writers) {
self->write_waiters++;
do pthread_cond_wait(&self->write, &self->lock);
while (self->readers || self->writers);
self->write_waiters--;
}
self->writers = 1;
pthread_mutex_unlock(&self->lock);
}
void writer_unlock(struct rwlock *self) {
pthread_mutex_lock(&self->lock);
self->writers = 0;
if (self->write_waiters)
pthread_cond_signal(&self->write);
else if (self->read_waiters)
pthread_cond_broadcast(&self->read);
pthread_mutex_unlock(&self->lock);
}
void rwlock_init(struct rwlock *self) {
self->readers = self->writers = self->read_waiters = self->write_waiters = 0;
pthread_mutex_init(&self->lock, NULL);
pthread_cond_init(&self->read, NULL);
pthread_cond_init(&self->write, NULL);
}
这种实现方式可以在有写锁存在的情况下不增加读锁数量,而是累加读等待数目。
三、一种Windows环境下的实现方法
在Windows环境下实现方式也是差不多的,参考如下实例:
typedef struct _RWLock
{
int count;
int state;
HANDLE hRead;
HANDLE hWrite;
} RWLock;
typedef enum /* 枚举读写状态 */
{
STATE_EMPTY = 0,
STATE_READ,
STATE_WRITE
};
RWLock* create_read_write_lock(HANDLE hRead, HANDLE hWrite)
{
RWLock* pRwLock = NULL;
assert(NULL != hRead && NULL != hWrite);
pRwLock = (RWLock*)malloc(sizeof(RWLock));
pRwLock->hRead = hRead;
pRwLock->hWrite = hWrite;
pRwLock->count = 0;
pRwLock->state = STATE_EMPTY;
return pRwLock;
}
void read_lock(RWLock* pRwLock)
{
assert(NULL != pRwLock);
WaitForSingleObject(pRwLock->hRead, INFINITE);
pRwLock->count ++;
if(1 == pRwLock->count){
WaitForSingleObject(pRwLock->hWrite, INFINITE);
pRwLock->state = STATE_READ;
}
ReleaseMutex(pRwLock->hRead);
}
void write_lock(RWLock* pRwLock)
{
assert(NULL != pRwLock);
WaitForSingleObject(pRwLock->hWrite, INFINITE);
pRwLock->state = STATE_WRITE;
}
void read_write_unlock(RWLock* pRwLock)
{
assert(NULL != pRwLock);
if(STATE_READ == pRwLock->state){
WaitForSingleObject(pRwLock->hRead, INFINITE);
pRwLock->count --;
if(0 == pRwLock->count){
pRwLock->state = STATE_EMPTY;
ReleaseMutex(pRwLock->hWrite);
}
ReleaseMutex(pRwLock->hRead);
}else{
pRwLock->state = STATE_EMPTY;
ReleaseMutex(pRwLock->hWrite);
}
return;
}
Windows环境下的实现方式要更加简单一些,不过这种方式的写操作一旦有读操作获取了锁,就只能等待所有读操作执行完了才行。如果一直都有读操作,那么写操作将会一直等待下去。
四、读写锁使用的经验总结
(1)读写锁的优势只有在多读少写、代码段运行时间长这两个条件下才会效率达到最大化;
(2)任何公共数据的修改都必须在锁里面完成;
(3)读写锁有自己的应用场所,选择合适的应用环境十分重要;
(4)编写读写锁很容易出错,需要多加练习;
(5)读锁和写锁一定要分开使用,否则达不到效果。
以上是关于多线程编程之读写锁的主要内容,如果未能解决你的问题,请参考以下文章