读写锁
互斥锁与读写锁的区别
同样都是锁,互斥锁与读写锁之间什么区别和联系?
互斥锁:当使用互斥锁时,每次只能有一个线程拿到锁,进入临界区,访问临界资源。
在对临界资源的访问中,可以将访问方式份为两种:
(1)读取数据
(2)修改数据
我们对与临界资源加锁,是为了保护临界资源的安全性与正确性,防止多个线程在同一时间段中对临界资源同时修改。但是,读取数据时我们只对于数据进行读取,并不对与数据进行修改,所以不会对数据的安全性和准确性造成问题。
但是,对于互斥锁来说,它不管线程是读数据还是写数据,每次都只能有一个线程拿到锁,等访问完后再还回锁。
如果在遇到有许多大量线程仅读取数据,少量线程进行数据修改的情景,使用互斥锁,所有的读取数据的线程都必须排队挨个读取,与修改数据的线程并无区别。为了提高效率,出现了读写锁来适用该种场景,提高效率。
读写锁
读写锁分为两种锁,一种为读锁,用于读取数据,一种为写锁,用来修改数据。当某一线程专门来读取共享数据时就使用读锁,当某一新线程要修改共享数据时,便使用写锁。
读锁(共享锁):
- 读锁可以被多个线程同时拿到,当临界区被读锁锁住时,其他使用读锁的线程仍旧可以拿到锁,进入临界区读取临界资源。
- 当临界区被写锁锁住时,读锁去获得锁时,会被阻塞直到写锁解锁
写锁(独占锁):
- 写锁每次只能被一个线程拿到,当一个读者已经拿到写锁,进入临界区时,其他后面到达的不管是读锁还是写锁都会被阻塞到解锁
- 当一个写锁去访问临界资源,如果在临界区中仍有一个或多个读者在读取数据时或者有一个写者在进行写数据,写者将被阻塞到所有的读者访问完,将锁解锁后,再获得锁。
读写锁的优点:读锁的使用提高了高并发度,可以使多个读者在任意时刻高并发读取数据。也保证了写入者在写入数据时,不会被其他写入者与读取者干扰,保证了数据的正确性与安全性。
适合场景:当有大量线程频繁的读取数据,只有少量的的线程对数据进行修改时
读写锁函数
#include <pthread.h>
使用pthread_rwlock_t 类型声明一个读写锁变量
读写锁的初始化
int pthread_rwlock_init(pthread_rwlock_t *rwptr,pthread_rwlockattr_t *attr)
//初始化一个读写锁,第一个参数为锁变量指针,第二个参数为属性,当为NULL使用默认属性
int pthread_rwlock_destroy(pthread_rwlock_t *rwptr) //销毁读写锁
读写锁的获取与释放
int pthread_rwlock_rdlock(pthread_rwlock_t *rwptr) //读锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwptr) //写锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwptr) //将读锁或写锁解锁
尝试获取锁
当临界区被一个写锁锁住时,之后想要获得锁的读者和写者获得锁失败时都将被阻塞,所以,如果使用以下两个函数,如果获得锁失败时,会返回一个错误,并不会被阻塞。
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwptr)
//尝试获得读锁
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwptr)
//尝试获得写锁
测试实例:
#include <iostream>
#include <pthread.h>
#include <unistd.h>
const int pth_num = 5;
int number = 0;
pthread_rwlock_t rwlock;
void* write_routine(void* arg)
{//写者线程函数
while(1)
{
usleep(1000);
pthread_rwlock_wrlock(&rwlock);
std::cout<<"write change the number: "<<++number<<std::endl;
pthread_rwlock_unlock(&rwlock);
}
}
void* read_routine(void* arg)
{//读者线程函数
int i = *(int*)arg;
while(1)
{
pthread_rwlock_rdlock(&rwlock);
std::cout<<"reader "<<i<<"get the number: "<<number<<std::endl;
pthread_rwlock_unlock(&rwlock);
usleep(300);
}
delete (int*)arg;
}
int main()
{
pthread_t id[pth_num];
pthread_rwlock_init(&rwlock,NULL);
pthread_create(&id[0], NULL, write_routine, NULL);
for(int i = 1; i < pth_num; i++)
{
int* p = new int;
p = &i;
pthread_create(&id[i], NULL, read_routine, (void*)p);
usleep(550);
}
for(int i = 0; i < pth_num; i++)
{
pthread_join(id[i], NULL);
}
pthread_rwlock_destroy(&rwlock);
return 0;
}