linux——线程通信(2)

文章目录

  • 1.条件变量
    • 1.1 条件变量函数:
    • 1.2 pthread_cond_init 函数
    • 1.3 pthread_cond_destroy 函数
    • 1.4 pthread_cond_wait 函数
    • 1.5 pthread_cond_timedwait 函数
    • 1.6 pthread_cond_signal 函数
    • 1.7 pthread_cond_broadcast 函数
  • 2.生产者消费者模型
  • 3.条件变量的优点:
  • 4.信号量
  • 5.信号量函数:
    • 5.1 信号量基本操作:
    • 5.2 sem_init 函数
    • 5.3 sem_destroy 函数
    • 5.4 sem_wait 函数
    • 5.5 sem_post 函数
    • 5.6 sem_trywait 函数
    • 5.7 sem_timedwait 函数
  • 6.信号量举例
  • 7.生产者消费者信号量模型
  • 8.哲学家吃饭问题
  • 9.共享内存

1.条件变量

条件变量本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。

1.1 条件变量函数:

pthread_cond_init 函数
pthread_cond_destroy 函数
pthread_cond_wait 函数
pthread_cond_timedwait 函数
pthread_cond_signal 函数
pthread_cond_broadcast 函数
以上 6 个函数的返回值都是:成功返回 0, 失败直接返回错误号。
pthread_cond_t 类型 用于定义条件变量
pthread_cond_t cond;

1.2 pthread_cond_init 函数

初始化一个条件变量

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
参 2:attr 表条件变量属性,通常为默认值,传 NULL 即可
也可以使用静态初始化的方法,初始化条件变量:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

1.3 pthread_cond_destroy 函数

销毁一个条件变量

int pthread_cond_destroy(pthread_cond_t *cond);

1.4 pthread_cond_wait 函数

阻塞等待一个条件变量

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

函数作用:

  1. 阻塞等待条件变量 cond(参 1)满足
  2. 释放已掌握的互斥锁(解锁互斥量)相当于 pthread_mutex_unlock(&mutex) ,1.2.两步为一个原子操作。
  3. 当被唤醒,pthread_cond_wait 函数返回时,解除阻塞并重新申请获取互斥锁 pthread_mutex_lock(&mutex);

1.5 pthread_cond_timedwait 函数

限时等待一个条件变量

int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
参 3: 参看 man sem_timedwait 函数,查看 struct timespec 结构体。
struct timespec {time_t tv_sec; /* seconds */long tv_nsec; /* nanosecondes*/ 纳秒
}
形参 abstime:绝对时间。
如:time(NULL)返回的就是绝对时间。而 alarm(1)是相对时间,相对当前时间定时 1 秒钟。struct timespec t = {1, 0};
pthread_cond_timedwait (&cond, &mutex, &t); 只能定时到 1970 年 1 月 1 日 00:00:01 秒(早已经过去)正确用法:
time_t cur = time(NULL); 获取当前时间。
struct timespec t; 定义 timespec 结构体变量 t
t.tv_sec = cur+1; 定时 1 秒
pthread_cond_timedwait (&cond, &mutex, &t); 传参 参 APUE.11.6 线程同步条件变量小节
在讲解 setitimer 函数时我们还提到另外一种时间类型:
 struct timeval {time_t tv_sec; /* seconds */ 秒suseconds_t tv_usec; /* microseconds */ 微秒};

1.6 pthread_cond_signal 函数

唤醒至少一个阻塞在条件变量上的线程

int pthread_cond_signal(pthread_cond_t *cond);

1.7 pthread_cond_broadcast 函数

唤醒全部阻塞在条件变量上的线程

 int pthread_cond_broadcast(pthread_cond_t *cond);

2.生产者消费者模型

线程同步典型的案例即为生产者消费者模型,而借助条件变量来实现这一模型,是比较常见的一种方法。
假定有两个线程,一个模拟生产者行为,一个模拟消费者行为。
两个线程同时操作一个共享资源(一般称之为汇聚),生产向其中添加产品,消费者从中消费掉产品。
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
struct msg {struct msg *next;int num;
};
struct msg *head;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;void *consumer(void *p)
{struct msg *mp;for (;;) {pthread_mutex_lock(&lock);while (head == NULL) { //头指针为空,说明没有节点 可以为 if 吗pthread_cond_wait(&has_product, &lock);}mp = head; head = mp->next; //模拟消费掉一个产品pthread_mutex_unlock(&lock);printf("-Consume ---%d\n", mp->num);free(mp);sleep(rand() % 5);}
}void *producer(void *p)
{struct msg *mp;while (1) {mp = malloc(sizeof(struct msg));mp->num = rand() % 1000 + 1; //模拟生产一个产品printf("-Produce ---%d\n", mp->num);pthread_mutex_lock(&lock);mp->next = head;head = mp;pthread_mutex_unlock(&lock);pthread_cond_signal(&has_product); //将等待在该条件变量上的一个线程唤醒sleep(rand() % 5);}
}int main(int argc, char *argv[])
{pthread_t pid, cid;srand(time(NULL));pthread_create(&pid, NULL, producer, NULL);pthread_create(&cid, NULL, consumer, NULL);pthread_join(pid, NULL);pthread_join(cid, NULL);return 0;
}
zhaoxr@zhaoxr-ThinkPad-E450:~/pthread$ ./pthread_cond 
-Produce ---425
-Consume ---425
-Produce ---486
-Consume ---486
-Produce ---699
-Consume ---699
-Produce ---736
-Consume ---736
-Produce ---452
-Consume ---452
-Produce ---529
-Consume ---529
-Produce ---664
-Consume ---664
-Produce ---340
-Consume ---340
^C

3.条件变量的优点:

相较于 mutex 而言,条件变量可以减少竞争。
如直接使用 mutex,除了生产者、消费者之间要竞争互斥量以外,
消费者之间也需要竞争互斥量,但如果汇聚(链表)中没有数据,消费者之间竞争互斥锁是无意义的。
有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。

4.信号量

进化版的互斥锁(1 --> N)
由于互斥锁的粒度比较大,如果我们希望在多个线程间对某一对象的部分数据进行共享,
使用互斥锁是没有办法实现的,只能将整个数据对象锁住。
这样虽然达到了多线程操作共享数据时保证数据正确性的目的,却无形中导致线程的并发性下降。
线程从并行执行,变成了串行执行。与直接使用单进程无异。
信号量,是相对折中的一种处理方式,既能保证同步,数据不混乱,又能提高线程并发。

5.信号量函数:

sem_init 函数
sem_destroy 函数
sem_wait 函数
sem_trywait 函数
sem_timedwait 函数
sem_post 函数
以上 6 个函数的返回值都是:成功返回 0, 失败返回-1,同时设置 errno。(注意,它们没有 pthread 前缀)
sem_t 类型,本质仍是结构体。但应用期间可简单看作为整数,忽略实现细节(类似于使用文件描述符)。
sem_t sem; 规定信号量 sem 不能 < 0。头文件 <semaphore.h>

5.1 信号量基本操作:

sem_wait: 1. 信号量大于 0,则信号量-- (类比 pthread_mutex_lock)2. 信号量等于 0,造成线程阻塞
sem_post: 将信号量++,同时唤醒阻塞在信号量上的线程 (类比 pthread_mutex_unlock)
但,由于 sem_t 的实现对用户隐藏,所以所谓的++--操作只能通过函数来实现,而不能直接++--符号。
信号量的初值,决定了占用信号量的线程的个数。

5.2 sem_init 函数

初始化一个信号量

int sem_init(sem_t *sem, int pshared, unsigned int value);
参 1:sem 信号量
参 2:pshared 取 0 用于线程间;取非 0(一般为 1)用于进程间
参 3:value 指定信号量初值

5.3 sem_destroy 函数

销毁一个信号量

int sem_destroy(sem_t *sem);

5.4 sem_wait 函数

给信号量加锁 –

int sem_wait(sem_t *sem);

5.5 sem_post 函数

给信号量解锁 ++

int sem_post(sem_t *sem);

5.6 sem_trywait 函数

尝试对信号量加锁 – (与 sem_wait 的区别类比 lock 和 trylock)

int sem_trywait(sem_t *sem);

5.7 sem_timedwait 函数

限时尝试对信号量加锁 –

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
参 2:abs_timeout 采用的是绝对时间。
定时 1 秒:
time_t cur = time(NULL); 获取当前时间。
struct timespec t; 定义 timespec 结构体变量 t
t.tv_sec = cur+1; 定时 1 秒
t.tv_nsec = t.tv_sec +100; 
sem_timedwait(&sem, &t); 传参

6.信号量举例

#include<stdio.h>
#include <semaphore.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>sem_t sem;
void *func(void* arg){int i=(int)arg;while(1){sem_wait(&sem);printf("我是%d,我正在吃饭,当前座位剩余:%d\n",i,sem);sleep(2);sem_post(&sem);sleep(4);}return NULL;
}int main()
{pthread_t tid[10];sem_init(&sem,0,5);int i;for(i=0;i<10;i++){pthread_create(&tid[i],NULL,func,(void*)i);}for(i=0;i<10;i++){pthread_join(tid[i],NULL);}sem_destroy(&sem);return 0;
}
zhaoxr@zhaoxr-ThinkPad-E450:~/pthread$ ./sem
我是0,我正在吃饭,当前座位剩余:4
我是1,我正在吃饭,当前座位剩余:3
我是2,我正在吃饭,当前座位剩余:2
我是3,我正在吃饭,当前座位剩余:1
我是4,我正在吃饭,当前座位剩余:0
我是5,我正在吃饭,当前座位剩余:1
我是8,我正在吃饭,当前座位剩余:2
我是6,我正在吃饭,当前座位剩余:0
我是7,我正在吃饭,当前座位剩余:1
我是9,我正在吃饭,当前座位剩余:0
我是0,我正在吃饭,当前座位剩余:4
我是4,我正在吃饭,当前座位剩余:2
我是1,我正在吃饭,当前座位剩余:3
我是3,我正在吃饭,当前座位剩余:1
我是2,我正在吃饭,当前座位剩余:0
^C

7.生产者消费者信号量模型

使用信号量完成线程间同步,模拟生产者,消费者问题。

【sem_product_consumer.c】
规定: 如果□中有数据,生产者不能生产,只能阻塞。如果□中没有数据,消费者不能消费,只能等待数据。定义两个信号量:S 满 = 0, S 空 = 1 (S 满代表满格的信号量,S 空表示空格的信号量,程序起始,格子一定为空)

所以有:

T 生产者主函数 { sem_wait(S 空); //空格减1生产....sem_post(S 满); //大饼加1
}
T 消费者主函数 {sem_wait(S 满);//大饼减1消费....sem_post(S 空);//空格加1}
假设: 线程到达的顺序是:T 生、T 生、T 消。
那么: T 生 1 到达,将 S 空-1,生产,将 S 满+1
T 生 2 到达,S 空已经为 0, 阻塞
T 消 到达,将 S 满-1,消费,将 S 空+1三个线程到达的顺序是:T 生 1、T 生 2、T 消。而执行的顺序是 T 生 1、T 消、T 生 2
这里,S 空 表示空格子的总数,代表可占用信号量的线程总数-->1。其实这样的话,信号量就等同于互斥锁。
但,如果 S 空=2、3、4……就不一样了,该信号量同时可以由多个线程占用,不再是互斥的形式。
因此我们说信号量是互斥锁的加强版。

8.哲学家吃饭问题

让所有哲学家听到吃饭口令之后,都拿自己右手边的筷子,但是选定某一个哲学家A必须拿自己左手边的筷子;
这样子,A哲学家右边的哲学家B肯定可以第一个吃到饭,吃完饭放下筷子,下面一个人就可以吃饭了;
以此类推。。。

9.共享内存

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>int a=10;
void* func(void* arg)
{sleep(1);a=100;printf("我要修改a\n");sleep(2);return NULL;
}
int main()
{pthread_t tid;pthread_create(&tid,NULL,func,NULL);while(1){printf("%d\n",a);sleep(1);}pthread_join(tid,NULL);return 0;
}
zhaoxr@zhaoxr-ThinkPad-E450:~/pthread$ ./pthread_com
10
我要修改a
100
100
100
100
100
100
100
^C

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

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

相关文章

Hadoop(2):常见的MapReduce[在Ubuntu中运行!]

1 以词频统计为例子介绍 mapreduce怎么写出来的 弄清楚MapReduce的各个过程&#xff1a; 将文件输入后&#xff0c;返回的<k1,v1>代表的含义是&#xff1a;k1表示偏移量&#xff0c;即v1的第一个字母在文件中的索引&#xff08;从0开始数的&#xff09;&#xff1b;v1表…

springboot学习笔记-01-springboot-helloworld的编写以及原理初步了解(自动装配)

文章目录原理初探主程序关于spring boot&#xff0c;谈谈你的理解&#xff1a;微服务阶段原理初探 pom.xml spring-boot-dependencies&#xff1a;核心依赖在父工程中&#xff01;我们在写或者引入一些springboot依赖的时候&#xff0c;不需要指定版本&#xff0c;就因为有这…

2022年:机器人技术的重大革命

来源&#xff1a;ScienceAI编辑&#xff1a;萝卜皮一段时间以来&#xff0c;跟踪机器人技术发展的人已经注意到该领域的一场无声革命。虽然自动驾驶汽车占据了所有的头条新闻&#xff0c;但人工智能、机器视觉和机器学习的交叉领域正在迅速成为下一阶段机器人技术的基础。通过将…

SpringBoot无法找到加载类 ,应用Feign其他服务无法package两类问题

无法找到加载类 1.如果在一系列简单的故障&#xff08;有没有加注解或者有没有依赖有没有全面&#xff09;&#xff0c;或者说加载类有没有在根目录src/main/java/(包名)的直接路径下 如果这些都没有 重点来了 把你 .idea 文件删了&#xff0c;运行 2.第二类问题 先把你要…

陈天奇高赞文章:新一代深度学习编译技术变革和展望

来源&#xff1a;机器之心作者&#xff1a;陈天奇陈天奇是机器学习领域著名的青年华人学者之一&#xff0c;本科毕业于上海交通大学ACM班&#xff0c;博士毕业于华盛顿大学计算机系&#xff0c;研究方向为大规模机器学习。在本文中&#xff0c;陈天奇回答了目前深度学习编译技术…

设计模式——桥模式

文章目录1.“单一职责”模式2.动机&#xff08;Motivation&#xff09;3.模式定义4.要点总结5.代码对比1.“单一职责”模式 在软件组件的设计中&#xff0c;如果责任划分的不清晰&#xff0c;使用继承得到的 结果往往是随着需求的变化&#xff0c;子类急剧膨胀&#xff0c;同时…

操作系统欢乐笔记-01-带你推开操作系统的大门(雾)

B站-操作系统-哈尔并工业大学-劝退警告223 文章目录1.什么是操作系统&#xff1f;什么是操作系统&#xff1f;小目标&#xff1f;2.揭开钢琴的盖子熟悉的win开机画面从白纸到图灵机冯-诺依曼一拍脑袋瓜&#xff0c;他说摁下开机键这段不是rap 223 劝退警告emmmmm1.什么是操作系…

oracle Sql语句分类

dml语句&#xff1a;数据操作语句【insert&#xff0c;update&#xff0c;delete】 ddl语句&#xff1a;数据定义语言【create table&#xff0c;drop table】 dql语句&#xff1a;数据查询语句【select】 dtl语句&#xff1a;数据控制语言【commit&#xff0c;rollback】 pack…

2022年值得关注的22项新兴技术

来源&#xff1a;参考消息网英国《经济学人》网站11月8日发表题为《下一个是什么&#xff1f;2022年值得关注的22项新兴技术》的文章。在文章列举的22项新技术中&#xff0c;既有今年大热的“元宇宙”、太空旅游、脑机接口&#xff0c;也有备受期待的量子计算、艾滋病病毒疫苗……

分别安装搭建lamp服务环境

一、 分别安装搭建lamp服务环境 准备工作&#xff1a; 1、配置防火墙&#xff0c;开启80端口、3306端口vi /etc/sysconfig/iptables-A INPUT -m state –state NEW -m tcp -p tcp –dport 80 -j ACCEPT #允许80端口通过防火墙-A INPUT -m state –state NEW -m tcp -p tcp –dp…

springdata学习笔记-01-helloworld-(暂时不全223)

第一天 orm思想和hibernate以及jpa的概述和jpd的基本操作 传统jdbc操作 获取链接创建statement对象可以对占位符赋值发送查询 操作繁琐占位符赋值麻烦 orm思想 主要目的&#xff1a;操作实体类就相当于操作数据库表建立两个映射关系 实体类和表的映射关系实体类中属性和表中…

oracle函数 INITCAP(c1)

【功能】返回字符串并将字符串的第一个字母变为大写&#xff0c;其它字母小写; 【参数】c1字符型表达式 【返回】字符型 【示例】 SQL> select initcap(smith abc aBC) upp from dual; UPP ----- Smith Abc Abc转载于:https://www.cnblogs.com/fanweisheng/p/11119958.html

设计模式——抽象工厂

文章目录1.“对象创建”模式2.动机&#xff08;Motivation&#xff09;3.模式定义4.要点总结5.代码对比1.“对象创建”模式 通过“对象创建” 模式绕开new&#xff0c;来避免对象创建&#xff08;new&#xff09;过程中所导致的紧耦合&#xff08;依赖具体类&#xff09;&…

nginx学习笔记-01nginx入门,环境搭建,常见命令

nginx学习笔记-01nginx入门&#xff0c;环境搭建&#xff0c;常见命令 文章目录nginx学习笔记-01nginx入门&#xff0c;环境搭建&#xff0c;常见命令1.nginx的基本概念2.nginx的安装&#xff0c;常用命令和配置文件3.nginx配置实例1.nginx的基本概念 nginx是什么&#xff0c;做…

MIT博士用概率编程让AI和人类一样看三维|NeurIPS 2021

来源&#xff1a;机器学习研究组订阅人与AI之间最大的区别就是对常识的利用&#xff01;无论各种AI模型在各大排行榜以何种性能超越了人类&#xff0c;它们在常识的利用上仍然远远不及人类&#xff0c;而这也正是目前AI研究中需要面临的一个巨大的挑战。对于自然语言处理的研究…

Unity Shader 2D水流效果

水流的模拟主要运用了顶点变换和纹理动画的结合&#xff1b; 顶点变换中&#xff0c;利用正弦函数模拟河流的大致形态&#xff0c;例如波长&#xff0c;振幅等。 纹理动画中&#xff0c;将纹理坐标朝某一方向持续滚动以形成流动的效果。 脚本如下&#xff1a; 1 Shader "M…

2022年智能家居十大预测新鲜出炉:全屋智能驶入快车道?健身镜成新宠……

来源&#xff1a;物联网智库 2021年接近尾声&#xff0c;这一年&#xff0c;新冠病毒仍旧没有离开地球&#xff0c;而在疫情常态化、工作与生活回归正轨之余&#xff0c;人们对于网络与虚拟世界的依赖度也陡然骤增。这一转变无疑将极大拉动消费端的数字化产业发展&#xff0c;除…

数据结构与算法——动态规划

文章目录1.内容概述2.爬楼梯2.1 题目描述2.算法思想2.3 代码实现3.打家劫舍3.1 题目描述3.2 算法思路3.3 代码实现4.最大子序和4.1 题目描述4.2 算法思路4.3 代码思路5. 零钱兑换5.1 题目描述5.2 算法思路5.3 代码实现6.三角形最小路径和6.1 题目描述6.2 算法思路6.3 代码实现7…

vue学习笔记-01-前端的发展历史(从后端到前端,再到前后端分离,再到全栈)

vue学习笔记-01-前端的发展历史&#xff08;从后端到前端&#xff0c;再到前后端分离&#xff0c;再到全栈&#xff09; 这篇文章是博主在看vue-前端发展简史的时候做的笔记&#xff0c;以供后续学习复习 文章目录vue学习笔记-01-前端的发展历史&#xff08;从后端到前端&#…

黑客帝国「缸中之脑」有眉目了?培养皿中百万人脑细胞学会打乒乓球,仅用了5分钟...

来源&#xff1a;机器之心编辑&#xff1a;张倩、杜伟既然生物神经元如此高效&#xff0c;为什么不拿来用呢&#xff1f;最新版本的《黑客帝国》还有两天才会上映&#xff0c;但最近的一些科技进展总让我们觉得&#xff0c;导演描述的世界似乎离我们越来越近了。其中一个进展来…