读写锁与互斥量的功能类似,对临界区的共享资源进行保护!互斥量一次只让一个线程进入临界区,读写锁比它有更高的并行性。读写锁有以下特点:
1.如果一个线程用读锁锁定了临界区,那么其他线程也可以用读锁来进入临界区,这样就可以多个线程并行操作。但这个时候,如果再进行写锁加锁就会发生阻塞,写锁请求阻塞后,后面如果继续有读锁来请求,这些后来的读锁都会被阻塞!这样避免了读锁长期占用资源,防止写锁饥饿!
2.如果一个线程用写锁锁住了临界区,那么其他线程不管是读锁还是写锁都会发生阻塞!
示例
创建4个线程,2个线程读锁,2个线程写锁,观察4个线程进入临界区的顺序
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
int count = 0;
void reader(void *arg)
{
char *name = (char*)arg;
while(1)
{
// 获取读写锁中的读锁
// int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock );
// 如果写入器未持有读锁,并且没有任何写入器基于该锁阻塞,则调用线程会获取读锁。
// 如果写入器未持有读锁,但有多个写入器正在等待该锁时,调用线程是否能获取该锁是不确定的。
// 如果某个写入器持有读锁,则调用线程无法获取该锁。
// 如果调用线程未获取读锁,则它将阻塞。
// 调用线程必须获取该锁之后,才能从pthread_rwlock_rdlock()返回。
// 如果在进行调用时,调用线程持有rwlock中的写锁,则结果是不确定的。
// 为避免写入器资源匮乏,允许在多个实现中使写入器的优先级高于读取器。
pthread_rwlock_rdlock(&rwlock);
printf("read_%s enters: %d\n", name, count);
usleep(1000);
printf("read_%s leave: %d\n", name, count);
// 解除锁定读写锁
pthread_rwlock_unlock(&rwlock);
usleep(1000);
}
}
void writer(void *arg)
{
char *name = (char*)arg;
while(1)
{
// 获取读写锁中的写锁
// int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock );
// 如果没有其他读取器线程或写入器线程持有读写锁rwlock,则调用线程将获取写锁
// 否则,调用线程将阻塞。
// 调用线程必须获取该锁之后,才能从pthread_rwlock_wrlock()调用返回
// 如果在进行调用时,调用线程持有读写锁(读锁或写锁),则结果是不确定的
// 为避免写入器资源匮乏,允许在多个实现中使写入器的优先级高于读取器
pthread_rwlock_wrlock(&rwlock);
count++;
printf("write_%s enters: %d\n", name, count);
usleep(1000);
printf("write_%s leave: %d\n", name, count);
// 解除锁定读写锁
pthread_rwlock_unlock(&rwlock);
usleep(2000);
}
}
int main()
{
pthread_t r, r1, w, w1;
pthread_create(&r, NULL, (void*)reader, (void*)"1");
pthread_create(&r1, NULL, (void*)reader, (void*)"2");
pthread_create(&w, NULL, (void*)writer, (void*)"1");
pthread_create(&w1, NULL, (void*)writer, (void*)"2");
pthread_join(r, NULL);
pthread_join(r1, NULL);
pthread_join(w,NULL);
pthread_join(w1, NULL);
// 销毁读写锁
pthread_rwlock_destroy(&rwlock);
return 0;
}