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


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

    1 读写锁属性类型

    #define __SIZEOF_PTHREAD_RWLOCKATTR_T 8
    typedef union
    {
      char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
      long int __align;
    } pthread_rwlockattr_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2 初始化读写锁属性对象

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

    3 销毁读写锁属性对象

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

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

    int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict attr,
                                      int *restrict pshared);
    int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared);
    
    • 1
    • 2
    • 3

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

    • 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);
    
    • 1
    • 2

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

    • 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;
    
    • 1
    • 2
    • 3

    7 销毁读写锁

    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    
    • 1

    8 读写锁之加读锁

    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
    
    • 1
    • 2

    9 读写锁之加写锁

    int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
    
    • 1
    • 2

    10 销毁读写锁

    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
    
    • 1

    11 案例:读写锁的使用

    • 源码

      #include 
      #include 
      #include 
      #include 
      
      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);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
    • 输出

      读线程(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)

  • 相关阅读:
    常用正则表达式(不定时更新)
    https
    javaEE7
    vscode | 开发神器vscode快捷键删除和恢复
    数据分析三剑客之一:Numpy详解及实战
    DP7340——192KHz双声道输入24位AD 转换器
    PHP:prim普里姆算法(附完整源码)
    线路位宽与 CPU 位宽
    Excel - 获取帮助信息,查找Sheet中和VBA里的可用函数
    mysql 切割字符串函数
  • 原文地址:https://blog.csdn.net/zhy29563/article/details/126670125