• Linux系统编程系列之条件变量


     Linux系统编程系列(16篇管饱,吃货都投降了!)

            1、Linux系统编程系列之进程基础

            2、Linux系统编程系列之进程间通信(IPC)-信号

            3、Linux系统编程系列之进程间通信(IPC)-管道

            4、Linux系统编程系列之进程间通信-IPC对象

            5、Linux系统编程系列之进程间通信-消息队列

            6、Linux系统编程系列之进程间通信-共享内存

            7、Linux系统编程系列之进程间通信-信号量组

            8、Linux系统编程系列之守护进程

            9、Linux系统编程系列之线程

            10、Linux系统编程系列之线程属性 

            11、Linux系统编程系列之互斥锁和读写锁

            12、Linux系统编程系列之线程的信号处理

            13、Linux系统编程系列之POSIX信号量

            14、Linux系统编程系列之条件变量

            15、Linux系统编程系列之死锁

            16、 Linux系统编程系列之线程池

    一、什么是条件变量

            条件变量是一种同步互斥机制,通常与互斥锁一起使用以实现线程之间的通信和同步。

    二、问题的引入

            先来看一个例子:小楠是一名在校学生,每个月都会从父母那里得到一笔生活费。现在她的钱花光了,想要去取钱。但是很显然取钱这样的事情不是想干就能干的,前提是卡里必须得有钱才行!于是小楠拿起手机一查发现:余额为¥0。现在她除了干瞪眼,唯一能干的事情也许只有一件:等。等到她爸妈汇了钱打电话通知她为止。

            但更进一步,即便是她爸妈汇了钱也打了电话通知了她,此刻她也不能一定保证能取到钱,因为与此同时她的众多兄弟姐妹(统统共用一个银行账号)很可能已经抢先一步将钱悉数取光了!因此当小楠收到爸妈的电话之后,需要再次确认是否有钱,才能取钱。

    三、使用场景

            (1)、 生产者-消费者模式:多个线程生产数据,多个线程消费数据。消费者等待条件变量,当生产者生产数据时唤醒消费者。

            (2)、 服务器程序:服务器可以使用条件变量来实现多个客户端之间的同步操作。当客户端请求数据时,服务器可能需要等待某些资源准备好后才能响应,此时可以使用条件变量来等待资源就绪。

            (3)、 任务管理:当有多个线程需要执行任务时,可以使用条件变量来通知空闲的线程执行任务

            (4)、 等待输入:当需要等待用户输入时,可以使用条件变量等待用户输入。

            总之,当需要等待某些条件满足时,使用条件变量是很常见的一种方式。条件变量提供了一个有效的机制来等待和通知多个线程,以实现共享资源间的同步和互斥

    四、相关的函数API接口

            1、初始化条件变量

    1. // 初始化条件变量
    2. int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
    3. // 接口说明
    4. 返回值:成功返回0,失败返回-1
    5. 参数cond:待初始化的条件变量
    6. 参数cond_attr:待初始化的条件变量的属性,一般始终为0

             2、销毁条件变量

    1. // 销毁条件变量
    2. int pthread_cond_destroy(pthread_cond_t *cond);
    3. // 接口说明
    4. 返回值:成功返回0,失败返回错误码
    5. 参数cond:待销毁的条件变量

            3、 进入等待队列

    1. // 阻塞等待
    2. int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
    3. // 接口说明
    4. 返回值;成功返回0,失败返回-1
    5. 参数cond:条件变量
    6. 参数mutex:需要获取的互斥锁
    7. // 有限定时间的等待
    8. int pthread_cond_timedwait(pthread_cond_t *cond,
    9. pthread_mutex_t *mutex,
    10. const struct timespec *abstime);
    11. // 接口说明
    12. 返回值;成功返回0,失败返回-1
    13. 参数cond:条件变量
    14. 参数mutex:需要获取的互斥锁
    15. 参数abstime:限定的时间

            4、唤醒等待队列 

    1. // 唤醒全部在条件变量等待队列的线程
    2. int pthread_cond_broadcast(pthread_cond_t *cond);
    3. // 唤醒一个在条件变量等待队列的线程
    4. int pthread_cond_signal(pthread_cond_t *cond);
    5. // 接口说明
    6. 返回值:成功返回0,失败返回-1
    7. 参数cond:条件变量

    五、案例

            使用条件变量结合互斥锁完成存钱和取钱的演示

    1. // 条件变量的案例
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. int money = 0;
    8. pthread_mutex_t data_mutex; // 定义互斥锁变量
    9. pthread_once_t data_mutex_once_init; // 函数单例初始化变量
    10. pthread_once_t data_mutex_once_destroy; // 函数单例销毁变量
    11. pthread_cond_t data_cond; // 定义条件变量
    12. pthread_once_t data_cond_once_init; // 函数单例初始化变量
    13. pthread_once_t data_cond_once_destroy; // 函数单例销毁变量
    14. // 初始化互斥锁data_mutex
    15. void data_mutex_init(void)
    16. {
    17. pthread_mutex_init(&data_mutex, NULL);
    18. }
    19. // 销毁互斥锁data_mutex
    20. void data_mutex_destroy(void)
    21. {
    22. pthread_mutex_destroy(&data_mutex);
    23. }
    24. // 初始化条件变量data_cond
    25. void data_cond_init(void)
    26. {
    27. pthread_cond_init(&data_cond, NULL);
    28. }
    29. // 销毁条件变量data_cond
    30. void data_cond_destroy(void)
    31. {
    32. pthread_cond_destroy(&data_cond);
    33. }
    34. // 线程1的例程函数,用来取钱
    35. void *get_routine(void *arg)
    36. {
    37. printf("I am recv_routine, my tid = %ld\n", pthread_self());
    38. // 设置线程分离
    39. pthread_detach(pthread_self());
    40. // 函数单例,本程序只会执行data_mutex_init()一次
    41. pthread_once(&data_mutex_once_init, data_mutex_init);
    42. // 函数单例,本程序只会执行data_cond_init()一次
    43. pthread_once(&data_cond_once_init, data_cond_init);
    44. while(1)
    45. {
    46. printf("wait mutex to get money...\n");
    47. pthread_mutex_lock(&data_mutex); // 阻塞等待有数据才可以申请成功,用来同步
    48. printf("get mutex to get money\n");
    49. // 判断余额是否大于100
    50. while(money < 100)
    51. {
    52. printf("get money fail, enter cond queue\n");
    53. // 这里先自动解锁data_mutex,等待被唤醒后,会自动上锁data_mutex
    54. pthread_cond_wait(&data_cond, &data_mutex);
    55. printf("wake up from cond queue\n");
    56. }
    57. money -= 100;
    58. printf("get money success\n");
    59. pthread_mutex_unlock(&data_mutex); // 解锁
    60. sleep(1); // 每隔1秒取100元
    61. }
    62. // 函数单例,本程序只会执行data_mutex_destroy()一次
    63. pthread_once(&data_mutex_once_destroy, data_mutex_destroy);
    64. // 函数单例,本程序只会执行data_cond_destroy()一次
    65. pthread_once(&data_cond_once_destroy, data_cond_destroy);
    66. }
    67. // 线程2的例程函数,用来存钱
    68. void *give_routine(void *arg)
    69. {
    70. printf("I am send_routine, my tid = %ld\n", pthread_self());
    71. // 函数单例,本程序只会执行data_mutex_init()一次
    72. pthread_once(&data_mutex_once_init, data_mutex_init);
    73. // 函数单例,本程序只会执行data_cond_init()一次
    74. pthread_once(&data_cond_once_init, data_cond_init);
    75. while(1)
    76. {
    77. pthread_mutex_lock(&data_mutex);
    78. money += 200;
    79. // printf()应该放到临界区外面,但是为了演示效果所以放在这里
    80. printf("current money is %d\n", money);
    81. pthread_mutex_unlock(&data_mutex); // 解锁,相当于给线程1发送信号
    82. pthread_cond_broadcast(&data_cond); // 唤醒条件等待队列中等待的线程
    83. sleep(3); // 每隔3秒存200元
    84. }
    85. // 函数单例,本程序只会执行data_mutex_destroy()一次
    86. pthread_once(&data_mutex_once_destroy, data_mutex_destroy);
    87. // 函数单例,本程序只会执行data_cond_destroy()一次
    88. pthread_once(&data_cond_once_destroy, data_cond_destroy);
    89. }
    90. int main(int argc, char *argv[])
    91. {
    92. pthread_t tid1, tid2;
    93. // 创建线程1,用来取钱
    94. errno = pthread_create(&tid1, NULL, get_routine, NULL);
    95. if(errno == 0)
    96. {
    97. printf("pthread create get_routine success, tid = %ld\n", tid1);
    98. }
    99. else
    100. {
    101. perror("pthread create get_routine fail\n");
    102. }
    103. // 创建线程2,用来存钱,线程拥有分离属性
    104. errno = pthread_create(&tid2, NULL, give_routine, NULL);
    105. if(errno == 0)
    106. {
    107. printf("pthread create give_routine success, tid = %ld\n", tid2);
    108. }
    109. else
    110. {
    111. perror("pthread create give_routine fail\n");
    112. }
    113. // 一定要加这个,否则主函数直接退出,相当于进程退出,所有线程也退出
    114. // 或者加上while(1)等让主函数不退出
    115. pthread_exit(0);
    116. return 0;
    117. }

    六、总结

            条件变量通常用于多线程间共享资源的同步访问,一般要配合互斥锁来使用,可以结合案例来加深对条件变量的理解。

  • 相关阅读:
    2023最新SSM计算机毕业设计选题大全(附源码+LW)之java拼车平台0k47u
    C++的类和对象(六):友元、内部类
    阿里 P9 用 500 多页手册完成双十一高并发秒杀系统,绝了
    java计算机毕业设计西安市城市绿地管理系统源码+数据库+系统+lw文档+mybatis+运行部署
    DBeaver使用
    【python桌面应用设计】tkinter库 02. 常用控件和属性
    git学习(一)
    Linux sed工具的使用
    coco数据集json文件转换成YOLO版的txt文件代码
    闪光激光雷达实现无人驾驶导航
  • 原文地址:https://blog.csdn.net/AABond/article/details/133418980