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


    1 互斥属性类型

    #define __SIZEOF_PTHREAD_MUTEXATTR_T 4
    typedef union
    {
        char __size[__SIZEOF_PTHREAD_MUTEXATTR_T];
        int __align;
    } pthread_mutexattr_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2 初始化与销毁互斥属性

    int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
    int pthread_mutexattr_init(pthread_mutexattr_t *attr);
    
    • 1
    • 2

    初始化与销毁互斥属性对象。

    • pthread_mutexattr_init以默认值初始化互斥属性对象。使用一个未初始化的互斥属性对象,将导致不确定性的行为。如果输入NULL,将使用默认的互斥属性值。
    • pthread_mutexattr_destroy销毁指定的互斥属性对象。一个已销毁的对象可以被重新初始化。使用一个已销毁的互斥属性对象,将导致不确定的行为。该函数在LinuxThread环境中的实现不做任何事情。
    • 一个互斥属性对象被修改或销毁不影响使用该互斥属性对象创建的互斥量

    3 互斥属性之锁类型

    int pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind);
    int pthread_mutexattr_getkind_np(const pthread_mutexattr_t *attr, int *kind);
    
    • 1
    • 2
    • 已弃用。使用pthread_mutexattr_settype & pthread_mutexattr_gettype
    int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
    int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *kind);
    
    • 1
    • 2

    LinuxThread仅支持一个互斥属性mutex kind。该值有以下可选值。

    • PTHREAD_MUTEX_FAST_NP

      快速互斥。默认值

    • PTHREAD_MUTEX_RECURSIVE_NP

      递归互斥

    • PTHREAD_MUTEX_ERRORCHECK_NP带错误检查的互斥

    • 后缀NP指示旋转是不可移植的(non-portable

      mutex kind用于决定在企图加锁一个已经处于锁定状态的的互斥时将如何做。

      • 如果为PTHREAD_MUTEX_FAST_NP将挂起当前线程。
      • 如果为PTHREAD_MUTEX_ERRORCHECK_NP立即返回,并携带错误码EDEADLK
      • 如果为PTHREAD_MUTEX_RECURSIVE_NP立即返回,并携带成功运行的返回码

    4 初始化与销毁互斥

    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                           const pthread_mutexattr_t *restrict attr);
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    • 1
    • 2
    • 3
    • 4
    • pthread_mutex_init

      • 如果参数为NULL,使用默认属性进行初始化。正常初始化后,互斥的状态变成已初始化且未锁定状态。

      • 企图初始化一个已初始化的互斥对象,将导致不确定性行为。

      • PTHREAD_MUTEX_INITIALIZER亦能完成互斥的初始化,但其不做错误检查。

    • pthread_mutex_destroy

      • 互斥被销毁后变成一个非法值。一个已销毁的互斥对象可被重新初始化。引用一个已销毁的互斥将出现不可预期的行为。

      • 销毁一个未锁定且已初始化的互斥是安全的。企图销毁一个锁定状态或被引用(例如pthread_cond_timedwaitpthread_cond_wait)将导致不确定行为。

    5 互斥之加锁解锁

    int pthread_mutex_lock(pthread_mutex_t *mutex);
    int pthread_mutex_trylock(pthread_mutex_t *mutex);
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    
    • 1
    • 2
    • 3
    • 如果互斥锁已被另一个线程锁定,调用线程将阻塞直到互斥锁可用。

    • pthread_mutex_lockpthread_mutex_trylock除加锁一个已经处于锁定状态的互斥时,前者进入阻塞状态,直接返回外,二者没区别。

    6 案例:互斥锁的使用

    使用互斥修改上一节中出错的样例,使其达到预期的效果。

    使用流程

    • 在每一个需要互斥的地方都需要先加锁,后解锁。
    • 加锁解锁必须成对使用,否则可能造成死锁
    • 最小代码块内进行加锁解锁,以提高程序运行效率
    • 源码

      #include 
      #include 
      #include 
      #include 
      
      pthread_mutex_t mutex;
      int g_num = 0;
      
      void *start_routine_01(void *ptr)
      {
          for (size_t i = 0; i < 10000; i++)
          {
              // 锁定最小单元
              pthread_mutex_lock(&mutex); // 锁定
              g_num++;
              pthread_mutex_unlock(&mutex); // 解锁
          }
          return (void *)NULL;
      }
      
      void *start_routine_02(void *ptr)
      {
          for (size_t i = 0; i < 10000; i++)
          {
              // 锁定最小单元
              pthread_mutex_lock(&mutex); // 锁定
              g_num++;
              pthread_mutex_unlock(&mutex); // 解锁
          }
          return (void *)NULL;
      }
      
      int main(int argc, char const *argv[])
      {
          pthread_mutexattr_t attr;      // 定义互斥属性
          pthread_mutexattr_init(&attr); // 初始化互斥属性
      
          pthread_mutex_init(&mutex, &attr); // 初始化互斥
          pthread_mutexattr_destroy(&attr);  // 销毁互斥属性
      
          pthread_t thread_id_01;
          pthread_t thread_id_02;
      
          pthread_create(&thread_id_01, NULL, start_routine_01, NULL);
          pthread_create(&thread_id_01, NULL, start_routine_02, NULL);
      
          pthread_join(thread_id_01, NULL);
          pthread_join(thread_id_02, NULL);
      
          printf("计算结果为: %d\n", g_num);
      
          printf("主线程即将退出...\n");
      
          pthread_mutex_destroy(&mutex); // 销毁互斥
          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
    • 结果

      计算结果为: 20000
      主线程即将退出…

  • 相关阅读:
    判断数组类型的方法(Array.isArray)以及Math数字对象
    Redis漏洞总结--未授权--沙箱绕过--(CNVD-2015-07557)&&(CNVD-2019-21763)&&(CVE-2022-0543)
    实现Ant Design Vue的modal可拖拽
    【CSDN|每日一练】幼稚班作业
    MyCat-web安装文档:安装Zookeeper、安装Mycat-web
    QT的安装和注册
    8.spark自适应查询-AQE之自适应调整Shuffle分区数量
    nginx反向代理,负载均衡配置
    【C++】继承
    数据结构:二叉树(2)
  • 原文地址:https://blog.csdn.net/zhy29563/article/details/126670094