linux线程同步-读写锁(reader-writer lock)

互斥锁是要么加锁要么不加锁,而且同一时刻只允许一个线程对其加锁。

 

读写锁有更高的并行性:

1、读写锁有三种状态:读模式下加速(共享)、写模式下加锁(独占)以及不加锁。

2、一次只有一个线程可以占有写模式下的读写锁;但是多个线程可以同时占有读模式下的读写锁。

3、读写锁在写加锁状态时,其他试图以写状态加锁的线程都会被阻塞。读写锁在读加锁状态时,如果有线程希望以写模式加锁时,必须阻塞,直到所有线程释放锁。

4、当读写锁以读模式加锁时,如果有线程试图以写模式对其加锁,那么读写锁会阻塞随后的读模式锁请求。以避免读锁长期占用,而写锁得不到请求。

 

读写锁适用场景:

读写锁适用于对数据结构读的次数远远大于写的次数的场合。

但前提条件是,加锁的持续时间很长。如果每次加锁的时间很短,那不如用互斥锁。

为什么会出现上述的情况呢。因为读写锁更新自己状态时必须是原子操作,在目前缺少精妙而且节省资源的同步方式之下,读写锁用一个字节来存储读的次数。因为读的次数必须原子更新,因此获取读锁与获取互斥量有着相同的资源需求,也就是有着相同的资源开销。

也就是说使用读写锁时,如果与互斥量获取锁的次数一样的话,资源开销上是一样的,而且还可能比互斥量稍微多一点。那为什么还要用读写锁呢?在一些特殊情况下,获取锁的时间比较长,这个时候用读写锁就可以让多个线程并发的去读,从而提高效率。但是这些特殊的任务还需要满足另外一个特点——读的次数远多于写。如果读写次数差不多,一次读一次写,那和用互斥量几乎一样。如果锁持有的时间很短,读的时候只读取几十个字节的内存,还用读写锁,那么可能会造成系统性能的下降,既然读取已经很快,还有必要并发的读吗?

综上,在使用读写锁时,一定要仔细分析应用场景。

创建读写锁API:

1、初始化读写锁:

pthread_rwlock_t mtx = PTHREAD_RWLOCK_INITIALIZER;

或者
 

#include
int pthread_rwlock_init( pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);

参数一:创建的读写锁

参数二:读写锁属性,NULL代表读写锁的各种属性取默认值

返回值:初始化成功返回0,否则返回错误编号

 

2、加锁

#include
//读模式加锁
int pthread_rwlock_rdlock ( pthread_ rwlock_t * rwlock);
//写模式加锁
int pthread_rwlock_wrlock ( pthread_ rwlock_t * rwlock);
参数一:创建的读写锁
返回值:加锁成功返回0,否则返回错误编号

 

尝试加锁:

#include
//读模式加锁,如果无法获取锁时,返回EBUSY
int pthread_ rwlock_tryrdlock( pthread_ rwlock_t * rwlock);
//写模式加锁,如果无法获取锁时,返回EBUSY
int pthread_ rwlock_trywrlock( pthread_ rwlock_t * rwlock);
返回值:加锁成功返回0;否则返回EBUSY

 

带超时的加锁:

提供了带有超时的读写锁加锁函数,使应用程序在获取读写锁时避免陷入永远阻塞的状态。当线程试图访问一个已经加锁的读写锁时,允许设置线程阻塞时间。若达到超时时间,则返回错误码ETIMEOUT。

#include
int pthread_ rwlock_ timedrdlock( pthread_ rwlock_t *restrict rwlock, const struct timespec *restrict tsptr );
int pthread_ rwlock_ timedwrlock( pthread_ rwlock_t *restrict rwlock, const struct timespec *restrict tsptr );
参数1:创建的读写锁
参数二:超时时间,线程愿意等待的绝对时间
(与相对时间对比而言,指定在时间X之前可以等待,而不是愿意阻塞Y秒),
用timespec结构来表示的(用秒和纳秒来描述时间)。返回值:加锁成功返回0,失败返回EBUSY

 

3、解锁

#include
int pthread_ rwlock_unlock( pthread_ rwlock_t * rwlock);
参数1:读写锁
返回值:解锁成功返回0;否则返回错误编号

 

4、销毁读写锁

#include
int pthread_ rwlock_destroy( pthread_ rwlock_t * rwlock );
返回值:销毁成功返回0,否则返回错误编号

#include 
#include 
#include 
#define MAXLEN 1024
pthread_rwlock_t mutex; 
char buf[MAXLEN] = "";
void* read_data_one()
{
    pthread_rwlock_rdlock(&mutex);
    int i = 0;
    for(i = 0; i < 5; i++)
    {
        printf("read one\n");
        sleep(1);
    } 
    pthread_rwlock_unlock(&mutex);
   
    pthread_exit(NULL);
    return;
}

void* read_data_two()
{
    pthread_rwlock_rdlock(&mutex);
    int i = 0;
    for(i = 0; i < 5; i++)
    {
        printf("read two\n");
        sleep(1);
    } 
    pthread_rwlock_unlock(&mutex);

    pthread_exit(NULL);
    return;
}

void* write_data_one()
{
    sleep(1);
    pthread_rwlock_wrlock(&mutex);
    int i;
    for(i = 0; i < 5; i++)
    {
        printf("write one\n");
        strcat(buf, "hello write data one\n");
        sleep(1);
    }
    pthread_rwlock_unlock(&mutex);
    pthread_exit(NULL);
    return;
}
void* write_data_two()
{
    sleep(1);
    pthread_rwlock_wrlock(&mutex);
    int i;
    for(i = 0; i < 5; i++)
    {
        printf("write two\n");
        strcat(buf, "hello write data two\n");
        sleep(1);
    }
    pthread_rwlock_unlock(&mutex);
    pthread_exit(NULL);
    return;
}

int main()
{
    pthread_t t1;
    pthread_t t2;
    pthread_t t3;
    pthread_t t4;
    pthread_rwlock_init(&mutex, NULL); 

    pthread_create(&t1, NULL, write_data_one, NULL);
    pthread_create(&t2, NULL, write_data_two, NULL);
    pthread_create(&t3, NULL, read_data_one, NULL);
    pthread_create(&t4, NULL, read_data_two, NULL);
     
    pthread_join(t1, NULL);  
    pthread_join(t2, NULL);  
    pthread_join(t3, NULL);  
    pthread_join(t4, NULL);  

    printf("buf:\n%s\n", buf);
    pthread_rwlock_destroy(&mutex);

    while(1);
    return;
}

 

linux线程同步-读写锁(reader-writer lock)_第1张图片

我在两个写线程入口处加了sleep(1), 程序会先进行读操作,可以发现,两个读操作是同时执行的。但写操作会进行阻塞。

读写锁相比互斥锁而已增加了并发读的性能。再需要大量读操作,且时间消耗较多的情况下,使用读写锁。

 

参考:

https://blog.csdn.net/weixin_42039602/article/details/83064346

https://blog.csdn.net/electrocrazy/article/details/79016629

你可能感兴趣的:(进程间通信)