【PTHREAD】线程互斥与同步之读写锁

多线程对同一个变量进行操作时,总是有读有写。一般情况下,读的频率大于写的频率。多线程同时对一个变量进行读操作是被允许的,而同时对一个变量进行写操作或某些线程对变量进行读操作,另一些线程对变量进行写操作是不被允许的。如果使用互斥量,那同时只能有一个线程对变量进行操作,则影响的程序执行的效率。为此,pthread库提供了读写锁。

1 读写锁属性类型

#define __SIZEOF_PTHREAD_RWLOCKATTR_T 8
typedef union
{
  char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
  long int __align;
} pthread_rwlockattr_t;

2 初始化读写锁属性对象

int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
  • 以默认值初始化一个读写锁属性对象
  • 初始化一个已初始化的读写锁属性对象,将导致不确定性行为
  • 使用一个已销毁的或未初始化的读写锁属性对象 ,将导致不确定性行为
  • 在使用读写锁属性对象初始化一个或多个读写锁对象之后,修改或销毁该读写锁属性对象不影响已创建的读写锁对象

3 销毁读写锁属性对象

int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
  • 销毁一个读写锁属性对象
  • 一个已销毁的读写锁属性对象可以被重新初始化
  • 释放一个未初始化的读写锁属性对象,将导致不确定性行为

4 读写锁属性之进程共享属性

int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict attr,
                                  int *restrict pshared);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared);

获取或设置读写锁属性对象中的进程共享属性。进程共享属性可以设置为以下值:

  • PTHREAD_PROCESS_SHARED

    设置为该值,允许任何有权限访问的线程访问分配读写锁的内存,即使读写锁是在由多个进程共享的内存中分配的

  • PTHREAD_PROCESS_PRIVATE

    读写锁只能由与初始化读写锁的线程在统一进程内创建的线程操作。如果不同进程的线程尝试操作该读写锁,则行为是未定义。该值为默认值。

注意

设置或获取一个未初始化的读写锁属性对象,将导致不确定性行为。

5 读写锁属性之锁类型

int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t *attr, int *pref);

获取或设置读写锁属性对象中的锁类型属性。锁类型属性有以下可选值:

  • PTHREAD_RWLOCK_PREFER_READER_NP

    该属性是默认值。一个线程可以拥有多个读锁。根据The Single Unix Specification,当读取器尝试放置锁,但无写入锁的写入器正在等待时,此时的行为是未定义的。当锁类型被设置为PTHREAD_RWLOCK_PREFER_READER_NP时,则读写器优先。这意味着机器写入器正在等待,读取器将收到请求的锁。只要读取器一直存在,写入器将处于饥饿状态。

  • PTHREAD_RWLOCK_PREFER_WRITER_NP

    This is intended as the write lock analog of PTHREAD_RWLOCK_PREFER_READER_NP. This is ignored by glibc because the POSIX requirement to support recursive writer locks would cause this option to create trivial deadlocks; instead use PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP which ensures the application developer will not take recursive read locks thus avoiding deadlocks.

  • PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP

    Setting the lock kind to this avoids writer starvation as long as any read locking is not done in a recursive fashion.

6 初始化读写锁

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
                        const pthread_rwlockattr_t *restrict attr);
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

7 销毁读写锁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

8 读写锁之加读锁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

9 读写锁之加写锁

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

10 销毁读写锁

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

11 案例:读写锁的使用

  • 源码

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    
    int g_num = 0;
    pthread_rwlock_t rwlock;
    
    void *start_routine_01(void *ptr)
    {
        for (size_t i = 0; i < 5; i++)
        {
            pthread_rwlock_rdlock(&rwlock);
            for (size_t i = 0; i < 2; i++)
            {
                printf("读线程(%lu)获取全局变量当前值(%d)\n", pthread_self(), g_num);
                sleep(1);
            }
            pthread_rwlock_unlock(&rwlock);
            sleep(1);
        }
    
        return (void *)NULL;
    }
    
    void *start_routine_02(void *ptr)
    {
        for (size_t i = 0; i < 5; i++)
        {
            pthread_rwlock_rdlock(&rwlock);
            for (size_t i = 0; i < 2; i++)
            {
                printf("读线程(%lu)获取全局变量当前值(%d)\n", pthread_self(), g_num);
                sleep(1);
            }
            pthread_rwlock_unlock(&rwlock);
            sleep(1);
        }
    
        return (void *)NULL;
    }
    
    void *start_routine_03(void *ptr)
    {
        for (size_t i = 0; i < 5; i++)
        {
            pthread_rwlock_wrlock(&rwlock);
            printf("写线程(%lu)设置全局变量当前值(%d)\n", pthread_self(), ++g_num);
            pthread_rwlock_unlock(&rwlock);
            sleep(2);
        }
    
        return (void *)NULL;
    }
    
    int main(int argc, char const *argv[])
    {
        pthread_rwlockattr_t attr;
        pthread_rwlockattr_init(&attr);
        pthread_rwlock_init(&rwlock, &attr);
        pthread_rwlockattr_destroy(&attr);
    
        pthread_t thread_id_01, thread_id_02, thread_id_03;
        pthread_create(&thread_id_01, NULL, start_routine_01, NULL);
        pthread_create(&thread_id_02, NULL, start_routine_02, NULL);
        pthread_create(&thread_id_03, NULL, start_routine_03, NULL);
    
        pthread_join(thread_id_01, NULL);
        pthread_join(thread_id_02, NULL);
        pthread_join(thread_id_03, NULL);
    
        pthread_rwlock_destroy(&rwlock);
        exit(EXIT_SUCCESS);
    }
    
  • 输出

    读线程(140456693409536)获取全局变量当前值(0)
    读线程(140456685016832)获取全局变量当前值(0)
    读线程(140456693409536)获取全局变量当前值(0)
    读线程(140456685016832)获取全局变量当前值(0)
    写线程(140456676624128)设置全局变量当前值(1)
    读线程(140456685016832)获取全局变量当前值(1)
    读线程(140456693409536)获取全局变量当前值(1)
    读线程(140456685016832)获取全局变量当前值(1)
    读线程(140456693409536)获取全局变量当前值(1)
    写线程(140456676624128)设置全局变量当前值(2)
    读线程(140456685016832)获取全局变量当前值(2)
    读线程(140456693409536)获取全局变量当前值(2)
    读线程(140456685016832)获取全局变量当前值(2)
    读线程(140456693409536)获取全局变量当前值(2)
    写线程(140456676624128)设置全局变量当前值(3)
    读线程(140456685016832)获取全局变量当前值(3)
    读线程(140456693409536)获取全局变量当前值(3)
    读线程(140456685016832)获取全局变量当前值(3)
    读线程(140456693409536)获取全局变量当前值(3)
    写线程(140456676624128)设置全局变量当前值(4)
    读线程(140456685016832)获取全局变量当前值(4)
    读线程(140456693409536)获取全局变量当前值(4)
    读线程(140456685016832)获取全局变量当前值(4)
    读线程(140456693409536)获取全局变量当前值(4)
    写线程(140456676624128)设置全局变量当前值(5)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhy29563

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值