条件变量--使两个线程实现交替打印

一、介绍

什么是条件变量?

条件变量(Condition Variable)是多线程编程中用于线程间通信和同步的一种机制。它通常与互斥锁(Mutex)一起使用,用于解决线程竞争和避免忙等待的问题。(条件变量不能单独使用)

条件变量解决的主要问题是当一个线程需要等待某个条件变成真时,它可以释放互斥锁,让其他线程有机会执行。当条件变成真时,线程可以重新获得互斥锁并继续执行。能提高线程的效率,避免了一些不必要的忙等待。

条件变量通常与以下三个函数一起使用:

pthread_cond_init: 初始化条件变量。

int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);

pthread_cond_wait: 在等待条件变为真的同时释放互斥锁,将线程挂起。

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

pthread_cond_signal / pthread_cond_broadcast: 用于通知等待条件变为真的线程。

  • pthread_cond_signal:通知等待队列中的一个线程。
  • pthread_cond_broadcast:通知等待队列中的所有线程。
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

使用条件变量时,几种典型的模式:

  1. 线程在互斥锁的保护下检查条件是否满足。
  2. 如果条件不满足,线程调用 pthread_cond_wait 来等待条件的变化,同时释放互斥锁。
  3. 当其他线程满足条件时,它们会调用 pthread_cond_signalpthread_cond_broadcast 来通知等待的线程。
  4. 被通知的线程醒来,重新获得互斥锁,再次检查条件是否满足。如果条件满足,它继续执行,否则它可能再次等待。

二、使用举例

题目描述:

使用两个线程实现交替打印“hello thread A”,“hello thread B”,要求不能出现连续的同一个线程进行打印。

解决方法:

定义一个互斥锁(g_lock)用于保护共享资源,确保一次只有一个线程可以访问临界区,避免数据竞争;

定义两个条件变量(thread_a_cond、thread_b_cond)用于控制线程间的通信。

定义一个变量(g_is_my_turn)控制两个线程的执行顺序。

定义两个函数thread_a_start、thread_b_start,分别让两个线程来调用。

void *thread_a_start(void *arg)
{(void)arg;while (1){pthread_mutex_lock(&g_lock);while (g_is_my_turn == 1){pthread_cond_wait(&thread_a_cond, &g_lock);}printf("hello thread A\n");sleep(1);g_is_my_turn++;pthread_mutex_unlock(&g_lock);pthread_cond_signal(&thread_b_cond);}
}
  • arg 参数未被使用,因此在函数体内使用 (void)arg; 这样的语句是为了消除编译器可能产生的未使用参数的警告。
  • 线程A通过互斥锁保护临界区。
  • g_is_my_turn 为1时,线程A等待条件变量 thread_a_cond,直到线程B通知。
  • 打印信息,休眠1秒,然后通过递增 g_is_my_turn 来通知线程B可以执行。
  • 解锁互斥锁,并通过条件变量 thread_b_cond 通知线程B。
void *thread_b_start(void *arg)
{(void)arg;while (1){sleep(1);pthread_mutex_lock(&g_lock);while (g_is_my_turn == 0){pthread_cond_wait(&thread_b_cond, &g_lock);}printf("hello thread B\n");sleep(1);g_is_my_turn--;pthread_mutex_unlock(&g_lock);pthread_cond_signal(&thread_a_cond);}
}
  • 线程B休眠1秒,然后通过互斥锁保护临界区。
  • g_is_my_turn 为0时,线程B等待条件变量 thread_b_cond,直到线程A通知。
  • 打印信息,休眠1秒,然后通过递减 g_is_my_turn 来通知线程A可以执行。
  • 解锁互斥锁,并通过条件变量 thread_a_cond 通知线程A。

主函数调用

  • 初始化互斥锁和条件变量。
  • 创建两个线程,分别执行线程A和线程B的函数。
  • 等待两个线程的结束。
  • 销毁互斥锁和条件变量。
int main()
{// 初始化pthread_mutex_init(&g_lock, NULL);pthread_cond_init(&thread_a_cond, NULL);pthread_cond_init(&thread_b_cond, NULL);// 定义两个线程pthread_t thread_a, thread_b;int ret = pthread_create(&thread_a, NULL, thread_a_start, NULL);if (ret < 0){perror("pthread_create error");return 0;}ret = pthread_create(&thread_b, NULL, thread_b_start, NULL);if (ret < 0){perror("pthread_create error");return 0;}pthread_join(thread_a, NULL);pthread_join(thread_b, NULL);pthread_mutex_destroy(&g_lock);pthread_cond_destroy(&thread_a_cond);pthread_cond_destroy(&thread_b_cond);return 0;
}

函数解析:

pthread_create 函数中,各个参数的含义如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
  1. thread 一个指向 pthread_t 类型的变量的指针,用于存储新创建线程的标识符。
  2. attr 一个指向 pthread_attr_t 类型的变量的指针,用于指定线程的属性。一般使用NULL。
  3. start_routine 是一个函数指针,指向线程的起始函数。这个函数必须返回 void * 类型,接受一个 void * 类型的参数。线程将从这个函数开始执行。该函数的参数只能有且一个void*类型。
  4. arg 传递给 start_routine 函数的参数,这里是一个 void * 类型的指针。通常,可以使用这个参数来向线程传递一些数据。

pthread_join(thread_a, NULL);

  • pthread_join 用于等待指定的线程终止。
  • hread_a 是被等待的线程。
  • 第二个参数是一个指向线程返回值的指针,这里使用 NULL 表示不关心线程的返回值。
  • 当调用 pthread_join 后,主线程会一直阻塞,直到指定的线程(thread_a)终止。

pthread_join(thread_b, NULL);同上。

pthread_mutex_destroy(&g_lock);

  • pthread_mutex_destroy 用于销毁互斥锁。
  • 在线程使用完互斥锁后,调用该函数释放相关资源。

pthread_cond_destroy(&thread_a_cond); pthread_cond_destroy(&thread_b_cond);

  • pthread_cond_destroy 用于销毁条件变量。
  • 在线程使用完条件变量后,调用该函数释放相关资源。

线程同步逻辑

线程A逻辑:

  • g_is_my_turn == 0,表示现在是线程A的执行时机。
  • 执行任务,通过递增 g_is_my_turn 使线程B可以执行。
  • 通过条件变量 thread_b_cond 来通知线程B。

线程B逻辑:

  • g_is_my_turn == 1,表示现在是线程B的执行时机。
  • 执行任务,通过递减 g_is_my_turn 使线程A可以执行。
  • 通过条件变量 thread_a_cond 来通知线程A。

运行截图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/221116.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】C++中的String类详解及模拟实现示例

文章目录 string类简介string类的基本用法string类的常用方法string类的优势 string类的模拟实现存储结构头文件string.h源文件string.cpp源文件test.cpp string类简介 string类简介在C编程中&#xff0c;字符串是一种非常常见的数据类型&#xff0c;用于存储文本信息。C标准库…

卫浴企业做网站的效果如何

卫浴产品无论工程还是家庭中都有较高需求度&#xff0c;相关品牌或经销商也不少&#xff0c;然而在实际经营中&#xff0c;卫浴品牌商家也面临着一些痛点&#xff1a; 1、品牌宣传拓客难 卫浴产品并不缺客户&#xff0c;但大小品牌众多&#xff0c;商家想要突围绝非易事&…

【Pytorch】学习记录分享2——Tensor基础,数据类型,及其多种创建方式

pytorch 官方文档 Tensor基础&#xff0c;数据类型&#xff0c;及其多种创建方式 1. 创建 Creating Tensor&#xff1a; 标量、向量、矩阵、tensor2. 三种方法可以创建张量&#xff0c;一是通过列表(list)&#xff0c;二是通过元组(tuple)&#xff0c;三是通过Numpy的数组(arra…

java 家教管理系统Myeclipse开发mysql数据库web结构jsp编程计算机网页项目

一、源码特点 java 家教管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

企业文档管理混乱?解密难题并找到高效解决方案!

由于大多数企业的内部文件都分散地存放在各员工电脑中&#xff0c;且没有使用文档管理系统&#xff0c;导致企业内部出现诸多文件安全隐患&#xff0c;例如&#xff1a; 1.文档分散存储在员工个人计算机中&#xff0c;当发生人事变动时文档常常遗失&#xff0c;且可能给企业造…

Ransac 算法的探索和应用

Ransac 算法python 应用和实现 Ransac 算法是一种常用的图像匹配算法&#xff0c;在参数估计领域也经常被使用到。针对估计各种曲线的鲁棒模型参数&#xff0c;效果显著。这里对ransac算法进行某些探索。 python program: import numpy as np import matplotlib.pyplot as p…

【Jmeter】Jmeter基础7-Jmeter元件介绍之后置处理器

后置处理器主要用于处理请求之后的操作&#xff0c;通常用来提取接口返回数据 2.7.1、JSON JMESPath Extractor 作用&#xff1a;可以通过JmesPath语法提取所需要的值使用场景&#xff1a;取样器返回格式为jsonJmesPath语法&#xff1a;参考https://jmespath.org/tutorial.htm…

怎么让mac右上角的时间不显示

时间成了影响工作效率和心态的一个东西&#xff0c;当我看不见时间的时候我是听命于我的平静而稳定的内心的&#xff0c;当时间时刻在我的眼前晃动的时候&#xff0c;我是慌乱而浮躁的&#xff0c;所以我决定在我工作的时候我不需要时间&#xff0c;我要听命于自己的状态&#…

2023自动化测试框架的设计原则你都知道吗?快来看!

1.代码规范 测试框架随着业务推进&#xff0c;必然会涉及代码的二次开发&#xff0c;所以代码编写应符合通用规范&#xff0c;代码命名符合业界标准&#xff0c;并且代码层次清晰。特别在大型项目、多人协作型项目中&#xff0c;如果代码没有良好的规范&#xff0c;那么整个框架…

自动化测试 (二) Web自动化测试原理

目前市面上有很多Web UI自动化测试框架&#xff0c;比如WatiN, Selinimu,WebDriver&#xff0c;还有VS2010中的Coded UI等等. 这些框架都可以操作Web中的控件&#xff0c;模拟用户输入&#xff0c;点击等操作&#xff0c;实现Web自动化测试。其实这些工具的原理都一样&#xf…

现代雷达车载应用——第2章 汽车雷达系统原理 2.3节 信号模型

经典著作&#xff0c;值得一读&#xff0c;英文原版下载链接【免费】ModernRadarforAutomotiveApplications资源-CSDN文库。 2.3 信号模型 雷达的发射机通常发出精心设计和定义明确的信号。然而&#xff0c;接收到的返回信号是多个分量的叠加&#xff0c;包括目标的反射、杂波…

1846_安全SPI

Grey 全部学习内容汇总&#xff1a;GitHub - GreyZhang/g_embedded: some embedded basic knowledge. 1846_安全SPI SPI是一种常见的通信方式&#xff0c;在汽车电子中比较常用。但是如果涉及到安全相关的设计&#xff0c;可能得考虑更多。而SPI协议本身没有很好的标准化&am…

819. 最常见的单词

819. 最常见的单词 Java&#xff1a;split() 过滤 class Solution {public String mostCommonWord(String paragraph, String[] banned) {String s paragraph.replaceAll("\\p{Punct}", " "); // 去除所有标点符号String arr[] s.split(" "…

Google视频广告的格式

Google是全球最大的搜索引擎和在线广告平台之一&#xff0c;也提供了广告服务&#xff0c;包括在其视频平台上展示视频广告。Google视频广告是一种强大的营销工具&#xff0c;可以帮助企业将品牌推广到更广泛的受众中。 Google视频广告的格式可以分为以下几种&#xff1a; 1、…

通俗易懂:插入排序算法全解析(C++)

插入排序算法是一种简单直观的排序算法&#xff0c;它的原理就像我们玩扑克牌时整理手中的牌一样。下面我将用通俗易懂的方式来解释插入排序算法的工作原理。 假设我们手上有一副无序的扑克牌&#xff0c;我们的目标是将它们从小到大排列起来。插入排序算法的思想是&#xff0…

vue整个页面可以拖拽导入文件

效果图 原理与源码 我们这里的思路是用ant组件库的upload组件&#xff0c;就是如下这个 用这个包裹住所有页面&#xff0c;你可以是包裹住App.vue&#xff0c;或者是你的homepage。但是这个涉及到一个问题&#xff0c;就是我们现在确实是可以拖拽导进来文件了&#xff0c;但是…

2.两数相加

借文引流&#xff1a;五点钟科技_大道至简系列,机器学习算法系列,学习经验分享-CSDN博客 欢迎大家阅览我的其它专栏。 题目&#xff1a; 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数…

台式扫描电镜与落地式扫描电镜详细对比

随着材料科学和微纳技术的快速发展&#xff0c;扫描电子显微镜已成为研究微观结构的一种重要工具。根据外形体积的不同&#xff0c;扫描电镜可以分为两大类:落地式扫描电镜和台式扫描电镜。本文将从探测器、易操作性、安装环境和价格等多个方面对两者进行比较。 一、发展历史 …

迎接更高效的数据安全合规与风险评估,美创科技DCAS正式商用发布!

数据安全合规与风险评估&#xff0c;是清晰数据安全合规与风险差距&#xff0c;实现可落地数据安全建设和持续改进的关键一环。然而实施起来&#xff0c;你的团队是否面临着这些烦恼&#xff1a; 数据安全合规要求繁多&#xff0c;难以全面掌握&#xff1f; 复杂评估流程带来效…

Unity | Shader基础知识(第四集:Shader结构体)

目录 一、本节介绍 1 上集回顾 2 本节介绍 二、结构体的需求 1 数据的接入 2 开始写结构体 三、unity封装好的结构体 1 unity封装好了很多结构体 2 如何使用封装好的结构体 四、下集预告 一、本节介绍 1 上集回顾 上一集&#xff0c;我们做了一个可以改变颜色的案例…