• 【PTHREAD】线程互斥与同步之条件变量


    互斥量能够保证在微观层面同一时刻仅有一个线程处理变量,但是不能保证执行顺序,更不能在某一时刻,一个线程通知另一个线程开始开始处理相关任务。

    1 条件变量属性类型

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

    2 初始化与销毁条件变量属性

    int pthread_condattr_destroy(pthread_condattr_t *attr);
    int pthread_condattr_init(pthread_condattr_t *attr);
    
    • 1
    • 2
    • pthread_condattr_init
      • 以默认值初始化条件变量属性。
      • 完成初始化以后,可使用该条件变量属性创建一个或多个条件变量。
      • 修改或销毁条件变量不影响使用该属性创建的条件变量。
      • 初始化一个已初始化的条件变量属性对象,将导致不确定性行为
    • pthread_condattr_destroy
      • 销毁指定条件变量属性对象
      • 执行完成后,条件变量属性对象编程未初始化状态
      • 已销毁的条件变量属性对象可以被再次初始化
      • 销毁一个已销毁的条件变量属性对象,将导致不确定性行为

    3 初始化与销毁条件变量

    int pthread_cond_destroy(pthread_cond_t *cond);
    int pthread_cond_init(pthread_cond_t *restrict cond,
                          const pthread_condattr_t *restrict attr);
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    
    • 1
    • 2
    • 3
    • 4

    初始化与销毁条件变量。

    • pthread_cond_init
      • 初始化一个条件变量
      • 如果条件变量属性设置为NULL,则以默认的条件变量属性进行初始化
      • 执行成功之后,条件变量变为已初始化状态
    • pthread_cond_destroy
      • 销毁指定的条件变量
      • 执行成功后,条件变量编程未初始化状态
      • 已销毁的条件变量可以被重新初始化
      • 销毁一个已销毁的条件变量,将导致不确定性行为
      • 销毁一个已初始化且未阻塞任何线程的条件变量是可行的
      • 企图销毁一个正在使用的条件变量将导致不确定性行为

    4 条件变量之阻塞

    int pthread_cond_timedwait(pthread_cond_t *restrict cond,
                               pthread_mutex_t *restrict mutex,
                               const struct timespec *restrict abstime);
    int pthread_cond_wait(pthread_cond_t *restrict cond,
                          pthread_mutex_t *restrict mutex);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 调用这两个函数将导致线程处于阻塞到一个条件变量
    • 当收到解锁信号后,阻塞将自动解除
    • pthread_cond_timedwait该函数使线程处于阻塞状态到指定的时间,如果在指定的时间内,还未等到信号,则退出等待

    5 条件变量之通知

    int pthread_cond_broadcast(pthread_cond_t *cond);
    int pthread_cond_signal(pthread_cond_t *cond);
    
    • 1
    • 2
    • pthread_cond_signal

      解除多个堵塞在同一个条件变量上的线程中的其中一个

    • pthread_cond_broadcast

      解除所有阻塞在通过条件变量上的所有线程

    6 案例:条件变量的使用

    • 源码

      线程一启动后进入阻塞状态,等待信号的到来。线程二在启动后,等待5秒后发出信号。

      #include 
      #include 
      #include 
      #include 
      
      pthread_mutex_t mutex;
      pthread_cond_t cond;
      
      void *start_routine_01(void *ptr)
      {
          printf("进入阻塞状态...\n");
          pthread_mutex_lock(&mutex);
          pthread_cond_wait(&cond, &mutex);
          printf("获得信号以解除阻塞状态...\n");
      
          pthread_mutex_unlock(&mutex);
          return (void *)NULL;
      }
      
      void *start_routine_02(void *ptr)
      {
          printf("等待5秒后发出解除信号...\n");
          sleep(5);
          pthread_mutex_lock(&mutex);
          pthread_cond_signal(&cond);
          printf("解除信号已发出...\n");
      
          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_condattr_t attr;      // 定义条件变量属性
              pthread_condattr_init(&attr); // 初始化条件变量属性
      
              pthread_cond_init(&cond, &attr); // 初始化条件变量
              pthread_condattr_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);
      
          pthread_mutex_destroy(&mutex); // 销毁互斥
          pthread_cond_destroy(&cond);   // 销毁条件变量
      
          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
    • 输出

      进入阻塞状态…
      等待5秒后发出解除信号…
      解除信号已发出…
      获得信号以解除阻塞状态…

  • 相关阅读:
    对称二叉树
    C现代方法(第10章)笔记——程序结构
    Linux centos7.0搭建Java开发环境(保姆级教程)
    D. Extreme Subtraction(差分)
    01-初识HTML和CSS
    算法练习(三)井字棋
    树莓派4b linux内核调试(jtag、kgdb)
    Linux学习-22-源码包安装、卸载和升级
    vue3实现导出Excel(2)
    【Linux】安装 Docker Registry
  • 原文地址:https://blog.csdn.net/zhy29563/article/details/126670112