线程同步--条件变量,信号量

生产者和消费者模型

image-20240512142852414

案例
/*生产者消费者模型(粗略的版本)
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>// 创建一个互斥量
pthread_mutex_t mutex;struct Node{int num;struct Node *next;
};// 头结点
struct Node * head = NULL;void * producer(void * arg) {// 不断的创建新的节点,添加到链表中,头插法while(1) {pthread_mutex_lock(&mutex);struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));newNode->next = head;head = newNode;newNode->num = rand() % 1000;printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());pthread_mutex_unlock(&mutex);usleep(100);}return NULL;
}void * customer(void * arg) {while(1) {pthread_mutex_lock(&mutex);// 保存头结点的指针struct Node * tmp = head;// 判断是否有数据if(head != NULL) {// 有数据head = head->next;printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());free(tmp);pthread_mutex_unlock(&mutex);usleep(100);} else {// 没有数据pthread_mutex_unlock(&mutex);}}return  NULL;
}int main() {pthread_mutex_init(&mutex, NULL);// 创建5个生产者线程,和5个消费者线程pthread_t ptids[5], ctids[5];for(int i = 0; i < 5; i++) {pthread_create(&ptids[i], NULL, producer, NULL);pthread_create(&ctids[i], NULL, customer, NULL);}for(int i = 0; i < 5; i++) {pthread_detach(ptids[i]);pthread_detach(ctids[i]);}while(1) {sleep(10);}pthread_mutex_destroy(&mutex);pthread_exit(NULL);return 0;
}

条件变量

条件变量不是锁,可以引起线程阻塞,某个条件满足以后阻塞线程或解除线程,他不能解决数据混乱的问题

条件变量的类型 pthread_cond_t

这些函数是 POSIX 线程(pthread)库的一部分,用于在 C 和 C++ 程序中提供线程处理能力。这里讨论的特定函数与条件变量有关,条件变量是同步原语,允许线程挂起执行并等待某些条件发生。

条件变量相关函数
1. pthread_cond_init

此函数初始化条件变量。它接受两个参数:

  • cond:指向要初始化的条件变量的指针。
  • attr:指向 pthread_condattr_t 结构的指针,该结构指定条件变量的任何属性。如果使用默认属性,此参数可以为 NULL。
pthread_cond_t cond;
pthread_cond_init(&cond, NULL);
2. pthread_cond_destroy

此函数销毁条件变量,释放与之关联的任何资源。在不再需要已初始化的条件变量时应调用它,以避免内存泄漏。

  • cond:指向要销毁的条件变量的指针。
pthread_cond_destroy(&cond);
3. pthread_cond_wait

此函数会阻塞调用线程,直到指定条件被触发。调用时必须由调用线程锁定互斥锁;pthread_cond_wait 会自动释放互斥锁并使调用线程在条件变量上阻塞。

  • cond:指向要等待的条件变量的指针。
  • mutex:指向互斥锁的指针。
pthread_cond_wait(&cond, &mutex);
4. pthread_cond_timedwait

pthread_cond_wait 类似,但带有超时。调用线程被阻塞,直到指定条件被触发或超时发生。

  • cond:指向条件变量的指针。
  • mutex:指向互斥锁的指针。
  • abstime:指向 timespec 结构的指针,指定绝对超时时间。
struct timespec ts;
// 设置 ts 为某个绝对时间
pthread_cond_timedwait(&cond, &mutex, &ts);
5. pthread_cond_signal

此函数至少解除阻塞在指定条件变量上的一个线程。它不释放互斥锁,也不会将互斥锁从信号线程转移给被唤醒的线程。

  • cond:指向要发信号的条件变量的指针。
pthread_cond_signal(&cond);
6. pthread_cond_broadcast

此函数解除阻塞所有当前在指定条件变量上阻塞的线程。与 pthread_cond_signal 不同,后者唤醒至少一个等待线程,broadcast 唤醒所有等待线程。

  • cond:指向要广播的条件变量的指针。
pthread_cond_broadcast(&cond);

这些函数是多线程编程中管理共享资源和同步不同线程执行流的重要工具。有效使用它们可以帮助避免复杂的多线程应用中的竞态条件和死锁。

案例
/*条件变量的类型 pthread_cond_tint pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);int pthread_cond_destroy(pthread_cond_t *cond);int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);- 等待,调用了该函数,线程会阻塞。int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);- 等待多长时间,调用了这个函数,线程会阻塞,直到指定的时间结束。int pthread_cond_signal(pthread_cond_t *cond);- 唤醒一个或者多个等待的线程int pthread_cond_broadcast(pthread_cond_t *cond);- 唤醒所有的等待的线程
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>// 创建一个互斥量
pthread_mutex_t mutex;
// 创建条件变量
pthread_cond_t cond;struct Node{int num;struct Node *next;
};// 头结点
struct Node * head = NULL;void * producer(void * arg) {// 不断的创建新的节点,添加到链表中while(1) {pthread_mutex_lock(&mutex);struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));newNode->next = head;head = newNode;newNode->num = rand() % 1000;printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());// 只要生产了一个,就通知消费者消费pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);usleep(100);}return NULL;
}void * customer(void * arg) {while(1) {pthread_mutex_lock(&mutex);// 保存头结点的指针struct Node * tmp = head;// 判断是否有数据if(head != NULL) {// 有数据head = head->next;printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());free(tmp);pthread_mutex_unlock(&mutex);usleep(100);} else {// 没有数据,需要等待// 当这个函数调用阻塞的时候,会对互斥锁进行解锁,当不阻塞的,继续向下执行,会重新加锁。pthread_cond_wait(&cond, &mutex);pthread_mutex_unlock(&mutex);}}return  NULL;
}int main() {pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond, NULL);// 创建5个生产者线程,和5个消费者线程pthread_t ptids[5], ctids[5];for(int i = 0; i < 5; i++) {pthread_create(&ptids[i], NULL, producer, NULL);pthread_create(&ctids[i], NULL, customer, NULL);}for(int i = 0; i < 5; i++) {pthread_detach(ptids[i]);pthread_detach(ctids[i]);}while(1) {sleep(10);}pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);pthread_exit(NULL);return 0;
}

信号量

信号量的类型 sem_t

sem_init

sem_init(sem_t *sem, int pshared, unsigned int value): 初始化一个未命名的信号量。参数sem是指向信号量结构的指针,pshared指示信号量是在进程间共享(非0值)还是线程间共享(0值),value是信号量的初始值。

sem_destroy

sem_destroy(sem_t *sem): 销毁一个未命名的信号量,释放它可能占用的资源。参数sem是指向信号量结构的指针。

sem_wait

sem_wait(sem_t *sem): 等待信号量。如果信号量的值大于0,它将减少1并立即返回。如果信号量的值为0,调用线程将阻塞,直到信号量值变为非0。

sem_trywait

sem_trywait(sem_t *sem): 尝试等待信号量。它是sem_wait的非阻塞版本,如果信号量的值为0,则立即返回错误,而不是阻塞。

sem_timedwait

sem_timedwait(sem_t *sem, const struct timespec *abs_timeout): 在指定的超时时间内等待信号量。如果信号量在给定时间内变为可用,它将减少信号量的值并返回;如果超时,则返回错误。

sem_post

sem_post(sem_t *sem): 通过增加信号量的值来释放信号量,如果有线程因等待这个信号量而阻塞,它将被唤醒。

sem_getvalue

sem_getvalue(sem_t *sem, int *sval): 获取信号量的当前值。参数sval是一个指针,用于存储读取的信号量值。

案例

/*信号量的类型 sem_tint sem_init(sem_t *sem, int pshared, unsigned int value);- 初始化信号量- 参数:- sem : 信号量变量的地址- pshared : 0 用在线程间 ,非0 用在进程间- value : 信号量中的值int sem_destroy(sem_t *sem);- 释放资源int sem_wait(sem_t *sem);- 对信号量加锁,调用一次对信号量的值-1,如果值为0,就阻塞int sem_trywait(sem_t *sem);int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);int sem_post(sem_t *sem);- 对信号量解锁,调用一次对信号量的值+1int sem_getvalue(sem_t *sem, int *sval);sem_t psem;sem_t csem;init(psem, 0, 8);init(csem, 0, 0);producer() {sem_wait(&psem);sem_post(&csem)}customer() {sem_wait(&csem);sem_post(&psem)}*/#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>// 创建一个互斥量
pthread_mutex_t mutex;
// 创建两个信号量
sem_t psem;
sem_t csem;struct Node{int num;struct Node *next;
};// 头结点
struct Node * head = NULL;void * producer(void * arg) {// 不断的创建新的节点,添加到链表中while(1) {sem_wait(&psem);pthread_mutex_lock(&mutex);struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));newNode->next = head;head = newNode;newNode->num = rand() % 1000;printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());pthread_mutex_unlock(&mutex);sem_post(&csem);}return NULL;
}void * customer(void * arg) {while(1) {sem_wait(&csem);pthread_mutex_lock(&mutex);// 保存头结点的指针struct Node * tmp = head;head = head->next;printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());free(tmp);pthread_mutex_unlock(&mutex);sem_post(&psem);}return  NULL;
}int main() {pthread_mutex_init(&mutex, NULL);sem_init(&psem, 0, 8);sem_init(&csem, 0, 0);// 创建5个生产者线程,和5个消费者线程pthread_t ptids[5], ctids[5];for(int i = 0; i < 5; i++) {pthread_create(&ptids[i], NULL, producer, NULL);pthread_create(&ctids[i], NULL, customer, NULL);}for(int i = 0; i < 5; i++) {pthread_detach(ptids[i]);pthread_detach(ctids[i]);}while(1) {sleep(10);}pthread_mutex_destroy(&mutex);pthread_exit(NULL);return 0;
}

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

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

相关文章

js的桶排序

桶排序&#xff08;Bucket Sort&#xff09;是一种分布式排序算法&#xff0c;它将元素分散到一系列桶中&#xff0c;然后对每个桶中的元素进行排序&#xff0c;并将所有的桶合并起来得到最终的排序结果。桶排序适用于输入的元素均匀分布在一个范围内的情况&#xff0c;它的时间…

DELL T630服务器iDRAC分辨率调整办法

对于Dell T630服务器的iDRAC分辨率调整&#xff0c;您需要登录到iDRAC的Web界面。以下是详细的步骤&#xff1a; 登录iDRAC&#xff1a;在浏览器中输入iDRAC的IP地址&#xff0c;然后使用用户名&#xff08;通常是“root”&#xff09;和密码登录。 导航到虚拟控制台&#xff…

Java中数组的特点、弊端与集合框架的使用

Java中数组的特点、弊端与集合框架的使用 在Java编程中&#xff0c;数组和集合框架&#xff08;Collections Framework&#xff09;都是用于存储数据的重要工具。然而&#xff0c;它们各自有其特点和适用场景。对于初学者来说&#xff0c;理解它们之间的区别和如何正确使用它们…

【C++】手搓读写ini文件源码

【C】手搓读写ini文件源码 思路需求&#xff1a;ini.hini.cppconfig.confmian.cpp 思路 ini文件是一种系统配置文件&#xff0c;它有特定的格式组成。通常做法&#xff0c;我们读取ini文件并按照ini格式进行解析即可。在c语言中&#xff0c;提供了模板类的功能&#xff0c;所以…

C++组合类

类的数据成员不但可以是基本类型&#xff0c;也可以是其它类的对象。 组合类就是指一个类包含其他类的对象作为该类的数据成员。 当组合类创建对象时&#xff0c;其中包含的各个数据成员对象应首先被创建。因此&#xff0c;在创建类的对象时&#xff0c;既要对本类的基本…

NumPy库与PyTorch库的异同点

目录 1.单位的创建和操作 1.创建 2.形状变换 2.数学和统计操作 1.矩阵乘法 2.广播 3.统计计算 3.GPU支持 4.在深度学习中的作用 5.应用范围 NumPy库为数组服务&#xff0c;PyTorch库为张量服务&#xff0c;这是最本质的区别。 1.单位的创建和操作 1.创建 NumPy:使…

三位球形模型应用

计算机图形学&#xff1a;三维球体模型可以用于建立真实感的三维场景&#xff0c;如游戏场景、建筑模型等。 工业设计&#xff1a;三维球体模型可以用于工业设计中的产品造型设计、表面细节设计等。 地球科学&#xff1a;三维球体模型可以用于地球科学领域中的地球内部结构建…

长安汽车:基于云器 Lakehouse 的车联网大数据平台建设

近年来随着智能汽车行业的迅速发展&#xff0c;数据也在呈爆炸式增长。长安大数据平台承接了长安在生产上大部分流量应用及日常生产业务应用。本文将分享长安汽车在车联网场景下大数据平台建设面临的一些挑战和具体落地的实践。 主要内容如下&#xff1a; 1. 背景介绍 2. 长…

Java中使用Comparator接口实现定制排序与对比

Java中使用Comparator接口实现定制排序与对比 在Java中&#xff0c;当我们需要对对象集合进行排序时&#xff0c;除了使用Comparable接口实现自然排序外&#xff0c;还可以使用Comparator接口来实现定制排序。Comparator接口允许我们为某个类的实例定义一种或多种比较方式&…

android图标底色问题,debug与release不一致

背景 在android 8&#xff08;sdk 26&#xff09;之前的版本&#xff0c;直接使用图片文件作为图标&#xff0c;开发时比较容易控制图标&#xff0c;但是不同的安卓定制版本就不容易统一图标风格了。 在android 8及之后的版本&#xff0c;图标对应的是ic_launcher.xml&#x…

【iOS】KVO

文章目录 前言一、KVO使用1.基本使用2.context使用3.移除KVO通知的必要性4.KVO观察可变数组 二、代码调试探索1.KVO对属性观察2.中间类3.中间类的方法3.dealloc中移除观察者后&#xff0c;isa指向是谁&#xff0c;以及中间类是否会销毁&#xff1f;总结 三、KVO本质GNUStep窥探…

基于51单片机的遥控开关仿真

基于51单片机的遥控开关设计 &#xff08;仿真&#xff0b;程序&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 本课题研究的是一款遥控开关&#xff0c;采用51单片机进行发射电路与接收电路的设计&#xff0c;发射电路由单片机最小系统及四个按键构成&am…

经典笔试题:快速排序 计数排序

Problem: 912. 排序数组 思路 &#x1f468;‍&#x1f3eb; 三叶题解 &#x1f496; AC&#xff1a;计数排序 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( n ) O(n) O(n) class Solution {public int[] sortArray(int[] nums) {int max -50001, min 50001;for (…

【半个月我拿下了软考证】软件设计师高频考点--系统化教学-关系模式

&#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;软件设计师考点暴击 ⭐&#x1f170;️进入狂砍分⭐ ⭐软件设计师高频考点文档&#xff0c; ⭐软件设计师高频考点专栏 ⭐软件设计师高频考点⭐ &#x1f3b6;&#xff08;A) 考点1,关系模式 考点&#xff1a; 三个模式相…

【JVM基础篇】类加载器分类介绍

文章目录 类加载器什么是类加载器类加载器的作用是什么应用场景类加载器的分类启动类加载器用户扩展基础jar包 扩展类加载器和应用程序类加载器扩展类加载器通过扩展类加载器去加载用户jar包&#xff1a; 应用程序加载器 Arthas中类加载器相关功能 文章说明 类加载器 什么是类…

[C++核心编程-01]----C++内存四区详细解析

目录 前言 正文 01-内存区域简介 02-全局区 03-栈区 04-堆区 05-new操作符 总结 前言 当程序运行时&#xff0c;操作系统会为程序分配一块内存空间&#xff0c;这块内存空间被划分为不同的区域&#xff0c;每个区域有其独特的作用…

赶紧收藏!2024 年最常见 100道 Java 基础面试题(四十二)

上一篇地址&#xff1a;赶紧收藏&#xff01;2024 年最常见 100道 Java 基础面试题&#xff08;四十一&#xff09;-CSDN博客 八十三、OSI的七层模型都有哪些&#xff1f; OSI&#xff08;Open Systems Interconnection&#xff09;参考模型是一个七层的网络通信模型&#xf…

python自定义x坐标名称

在画完图后加上 x[0.1,0.5,1.0,2.0,4.0,6.0,8.0] plt.xticks(x) import matplotlib.pyplot as pltx [1, 2, 3, 4, 5] y [2, 4, 6, 8, 10]plt.plot(x, y) plt.xticks(x, [A, B, C, D, E]) # 设置x轴坐标位置和标签 plt.show()要自定义x坐标名称&#xff0c;你可以使用matplo…

Unity图形图表XChart插件使用

最近做了一款数字孪生项目,其中涉及到了图形图表的应用,网上找了一下,找到了XChart插件,使用起来蛮方便的,不过还有待继续研究,很多细节性的知识点需要进行学习探索。以下是项目中的应用。 官方应用: ![](https://img-blog.csdnimg.cn/direct/ab9de8e84e7b4be4a50ea…

双指针算法(判断子序列,最长连续不重复子序列)

一.判断子序列 编程题 题目描述&#xff1a; 给定一个长度为 n 的整数序列 a1,a2,…,an 以及一个长度为 m 的整数序列 b1,b2,…,bm。 请你判断 a 序列是否为 b 序列的子序列。 子序列指序列的一部分项按原有次序排列而得的序列&#xff0c;例如序列 {a1,a3,a5} 是序列 {a1…