//最近在学习《高性能mysql》的时候,提到了读写锁,读锁是共享锁,写锁是排它锁,还有项目中也提到了读写锁的概念。进而想弄清楚,读写锁的实现机制,
//只有明白了读写锁的实现机制,才能明白读写锁对性能的影响
//本程序采用的是pthread_mutex_t互斥体+pthread_cond_t条件变量的方式去模拟读写锁的实现机制
//希望能够帮助刚刚接触pthread线程的朋友,如何去使用互斥体和条件变量来实现多线程协同工作
//编译方式gcc -o rwlock rwlock.c -lpthread
//作者在多线程领域也研究了一段时间,不过,在平时的项目中还是尽可能的避免使用锁,一方面加锁会降低程序的维护性,另外极易产生并发错误
//为了尽可能的做到无锁编程,需要结合pthread提供的线程特有资源来做。
//对于基本数据类型直接使用__thread关键字来实现线程独立的全局变量
//闲话少叙,本程序中需要注意volatile关键字修饰的变量,主要是防止编译器做优化。由于每个线程有自己独立的寄存器,当编译器做了优化之后,很有可能数据被加载
//到寄存器,并做了修改,也不会立即回写,当另一个线程将该数据加载到寄存器的时候,就不是最新的数据了,这样即使上锁,也不能保证数据不会被破坏
//由于作者资历不深,如果程序中有什么错误,希望大家多多指出,作者不甚感激
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define READ_THREAD_NUM 10
#define WRITE_THREAD_NUM 5
#define RES_LEN 10
#define log_error_by_errno(savedErrno) do { \
char errMsg[255]; \
strerror_r(savedErrno,errMsg,sizeof(errMsg)); \
printf("errno:[%d]----errMsg:[%s]---line:[%d]---function:[%s]\n",savedErrno,errMsg,__LINE__,__FUNCTION__); \
exit(EXIT_FAILURE); \
}while(0);
typedef struct Lock
{
pthread_mutex_t lock;
pthread_cond_t cond;
volatile int owners;//we must add the volatile key word
}Lock_t;
typedef struct RW_Lock
{
Lock_t rLock;
Lock_t wLock;
volatile int res[RES_LEN];
}RW_Lock_t;
void readLock(RW_Lock_t * prwLock)
{
int ret;
if((ret = pthread_mutex_lock(&prwLock->rLock.lock)))
{
log_error_by_errno(ret);
}
while(prwLock->rLock.owners != 0)
{
if((ret = pthread_cond_wait(&prwLock->rLock.cond,&prwLock->rLock.lock)))
{
log_error_by_errno(ret);
}
}
if((ret = pthread_mutex_unlock(&prwLock->rLock.lock)))
{
log_error_by_errno(ret);
}
//mark the write lock owner
if((ret = pthread_mutex_lock(&prwLock->wLock.lock)))
{
log_error_by_errno(ret);
}
prwLock->wLock.owners++;
printf("wlock.owners:[%d]---line:[%d]---function:[%s]--thread:[%u]\n",prwLock->wLock.owners,__LINE__,__FUNCTION__,pthread_self());
if((ret = pthread_mutex_unlock(&prwLock->wLock.lock)))
{
log_error_by_errno(ret);
}
}
void readUnLock(RW_Lock_t * prwLock)
{
int ret;
if((ret = pthread_mutex_lock(&prwLock->wLock.lock)))
{
log_error_by_errno(ret);
}
prwLock->wLock.owners--;
printf("wlock.owners:[%d]---line:[%d]---function:[%s]--thread:[%u]\n",prwLock->wLock.owners,__LINE__,__FUNCTION__,pthread_self());
if(prwLock->wLock.owners == 0)
{
if((ret = pthread_cond_signal(&prwLock->wLock.cond)))
{
log_error_by_errno(ret);
}
}
if((ret = pthread_mutex_unlock(&prwLock->wLock.lock)))
{
log_error_by_errno(ret);
}
}
void writeLock(RW_Lock_t * prwLock)
{
int ret;
if((ret = pthread_mutex_lock(&prwLock->rLock.lock)))
{
log_error_by_errno(ret);
}
prwLock->rLock.owners++;
printf("rlock.owners:[%d]---line:[%d]---function:[%s]--thread:[%u]\n",prwLock->rLock.owners,__LINE__,__FUNCTION__,pthread_self());
if((ret = pthread_mutex_unlock(&prwLock->rLock.lock)))
{
log_error_by_errno(ret);
}
if((ret = pthread_mutex_lock(&prwLock->wLock.lock)))
{
log_error_by_errno(ret);
}
while(prwLock->wLock.owners != 0)
{
if((ret = pthread_cond_wait(&prwLock->wLock.cond,&prwLock->wLock.lock)))
{
log_error_by_errno(ret);
}
}
}
void writeUnLock(RW_Lock_t * prwLock)
{
int ret;
if((ret = pthread_mutex_unlock(&prwLock->wLock.lock)))
{
log_error_by_errno(ret);
}
if((ret = pthread_mutex_lock(&prwLock->rLock.lock)))
{
log_error_by_errno(ret);
}
prwLock->rLock.owners--;
printf("rlock.owners:[%d]---line:[%d]---function:[%s]--thread:[%u]\n",prwLock->rLock.owners,__LINE__,__FUNCTION__,pthread_self());
if(prwLock->rLock.owners == 0)
{
if((ret = pthread_cond_signal(&prwLock->rLock.cond)))
{
log_error_by_errno(ret);
}
}
if((ret = pthread_mutex_unlock(&prwLock->rLock.lock)))
{
log_error_by_errno(ret);
}
}
void * readFun(void * param)
{
for(; ;)
{
srand(time(NULL));
RW_Lock_t * prwLock = (RW_Lock_t *)param;
readLock(prwLock);
//mark 进入临界区
//do something
printf("res:[");
int i = 0;
for(i = 0; i < sizeof(prwLock->res)/sizeof(prwLock->res[0]); ++i)
{
printf("%d\t",prwLock->res[i]);
}
printf("]\n");
//mark 退出临界区
readUnLock(prwLock);
sleep(rand() % 2 + 1);
}
return NULL;
}
void * writeFun(void * param)
{
for(; ;)
{
srand(time(NULL));
RW_Lock_t * prwLock = (RW_Lock_t *)param;
writeLock(prwLock);
//进入临界区
//do something
int index = rand()%RES_LEN;
prwLock->res[index] = rand() % RES_LEN;
//退出临界区
writeUnLock(prwLock);
sleep(rand() % 3 + 1);
}
return NULL;
}
int main(void)
{
pthread_t rthreadPool[READ_THREAD_NUM];
pthread_t wthreadPool[WRITE_THREAD_NUM];
RW_Lock_t rwLock;
RW_Lock_t * prwLock = &rwLock;
int ret;
if((ret = pthread_mutex_init(&prwLock->rLock.lock,NULL)))
{
log_error_by_errno(ret);
}
if((ret = pthread_cond_init(&prwLock->rLock.cond,NULL)))
{
log_error_by_errno(ret);
}
prwLock->rLock.owners = 0;
if((ret = pthread_mutex_init(&prwLock->wLock.lock,NULL)))
{
log_error_by_errno(ret);
}
if((ret = pthread_cond_init(&prwLock->wLock.cond,NULL)))
{
log_error_by_errno(ret);
}
prwLock->wLock.owners = 0;
memset(&prwLock->res,0,sizeof(prwLock->res));
int i = 0;
for(i = 0; i < READ_THREAD_NUM; ++i)
{
if((ret = pthread_create(&rthreadPool[i],NULL,readFun,prwLock)))
{
log_error_by_errno(ret);
}
}
for(i = 0; i <WRITE_THREAD_NUM; ++i)
{
if((ret = pthread_create(&wthreadPool[i],NULL,writeFun,prwLock)))
{
log_error_by_errno(ret);
}
}
for(i = 0; i < READ_THREAD_NUM; ++i)
{
pthread_join(rthreadPool[i],NULL);
}
for(i = 0; i < WRITE_THREAD_NUM; ++i)
{
pthread_join(wthreadPool[i],NULL);
}
return 0;
}