互斥锁、条件变量

一、互斥锁

1. 函数原型:

pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);  
pthread_mutex_destroy(pthread_mutex_t *mutex); 

分析:

  • pthread_mutex_t 类型,其本质是一个结构体,为简化理解,应用时可忽略其实现细节,简单当成整数看待。
  • pthread_mutex_t  mutex:变量mutex只有两种取值0、1;

函数一参数1:传出参数,调用时应传&mutex

  • restrict关键字:只用于限制指针,告诉编译器,所有修改该指针指向内存中内容的操作,只能通过本指针完成。不能通过除本指针以外的其他变量或指针修改。

函数一参数2:互斥属性。是一个传入参数,通常传NULL,选用默认属性(线程间共享).

  • 静态初始化:如果互斥锁mutex是静态分配的(定义在全局,或加了static关键字修饰),可以直接使用宏进行初始化。pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  • 动态初始化:局部变量应采用动态初始化, pthread_mutex_init(&mutex, NULL);

 

pthread_mutex_lock(pthread_mutex_t *mutex);
pthread_mutex_unlock(pthread_mutex_t *mutex);                                        

分析:

  • 函数1:没有被上锁,当前线程会将这把锁锁上;被锁上了,当前线程阻塞,锁被打开之后,线程解除阻塞(加锁。可理解为将mutex--(或-1))。
  • 函数2:同时将阻塞在该锁上的所有线程全部唤醒解锁(可理解为将mtex++(或+1)).

 

2. 测试代码:

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>void *tfn(void *arg)
{srand(time(NULL));while(1) {printf("hello ");sleep(rand() % 3); //模拟长时间操作共享资源,导致cpu易主,产生与时间有关的错误printf("word\n");sleep(rand() % 3);}return NULL;
}int main()
{pthread_t tid;srand(time(NULL));pthread_create(&tid, NULL, tfn, NULL);while(1) {printf("HELLO ");sleep(rand() % 3);printf("WORLD\n");sleep(rand() % 3);}return 0;
}

输出结果:

 

  3. 测试代码:

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>void *tfn(void *arg)
{srand(time(NULL));while(1) {printf("hello ");sleep(rand() % 3); //模拟长时间操作共享资源,导致cpu易主,产生与时间有关的错误printf("word\n");sleep(rand() % 3);}return NULL;
}int main()
{pthread_t tid;srand(time(NULL));pthread_create(&tid, NULL, tfn, NULL);while(1) {printf("HELLO ");sleep(rand() % 3);printf("WORLD\n");sleep(rand() % 3);}return 0;
}

输出结果

 

二、 条件变量

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

 

1. 函数原型:

pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t * attr);  //初始化一个条件变量
pthread_cond_destroy(pthread_cond_t *cond);     // 销毁一个条件变量   pthread_cond_signal(pthread_cond_t *cond);    // 唤醒至少一个阻塞在条件变量上的线程
pthread_cond_broadcast(pthread_cond_t *cond); // 唤醒全部阻塞在条件变量上的线程                             

 

2. 函数原型:

pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

分析:

函数作用:阻塞等待一个条件变量

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

 

3. 函数原型:

    函数作用:限时等待一个条件变量

pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex,  const struct timespec *restrict abstime);

参数3:

struct timespec 
{time_t tv_sec;  seconds 秒 long tv_nsec;    nanoseconds 纳秒
}

你形参:abstime:绝对时间

如:time(NULL)返回的就是绝对时间。而alarm(1)是相对时间,相当当前时间定时1秒钟。

struct timespec t = {1,  0};

pthread_cond_time(&cond, &mutex, &t);只能定时到1970年1月1日00:00:01秒(早已经过去)

正确用法:

time_t cur = time(NULL);   获取当前时间
struct timespec t;         定义timespec结构体变量
t.tv_sec = cur + 1;        定时一秒
pthread_cond_timewait(&cond, mutex, &t) 传参 

 

4. 函数原型:

pthread_cond_signal(pthread_cond_t *cond);    // 唤醒至少一个阻塞在条件变量上的线程
pthread_cond_broadcast(pthread_cond_t *cond); // 唤醒全部阻塞在条件变量上的线程

 

 

三、生产者消费者条件变量模型

线程同步典型的案例即为生产者消费者模型,而借助条件变量来实现这一案例,是比较常见的方法,假定有两个线程,一个模拟生产者行为,一个模拟消费者行为,两个线程同时操作一个共享资源(一般称之为汇聚),生产者向其中添加产品,消费者从中消费掉产品。

1. 测试代码:

#include <pthread.h> 
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>struct msg
{struct msg *next;int num;
};struct msg *head;
struct msg *mp;pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;  //静态初始化:一个条件变量和一个互斥量 
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;void *consumer(void *p)
{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)
{for (; ;) {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()
{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;
}

输出结果:

 【注意】:为什么用while循环而不用if呢?

一个生产者可能对应着多个消费者,生产者向队列中插入一条数据之后发出signal,然后各个消费者线pthread_cond_wait获取mutex后返回,当然,这里只有一个线程获取到了mutex,然后进行处理,其它线程会pending在这里,处理线程处理完毕之后释放mutex,刚才等待的线程中有一个获取mutex,如果这里用if,就会在当前队列为空的状态下继续往下处理,这显然是不合理的。

 

 

五、参考资料

1 深入解析条件变量(condition variables)
 

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

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

相关文章

Java面试你必须要知道的那些知识,深夜思考

如何提升自己的实力&#xff1f; Step 1&#xff1a;梳理自己的知识 对照下面这份学习大纲&#xff0c;梳理出自己的知识盲区&#xff0c;这份大纲里面的技术点完全对标P7岗的主流技术&#xff0c;因此这是一份很好的知识大纲笔记。 Step 2&#xff1a;查漏补缺&#xff0c;夯…

Java面试你必须要知道的那些知识,面试建议

二、面试题 面&#xff1a;考你几个红黑树的知识点&#x1f980; 红黑树的数据结构都用在哪些场景&#xff0c;有什么好处&#xff1f;红黑树的时间复杂度是多少&#xff1f;红黑树中插入新的节点时怎么保持平衡&#xff1f; 面&#xff1a;2-3树都是不没看&#xff0c;回去…

存储映射I/O(一)

一、存储映射I/O 存储映射I/O使一个磁盘文件与存储空间中的一个缓冲区映射&#xff0c;于是当从缓冲区中取数据&#xff0c;就相当于读文件中的相应字节。于此类似&#xff0c;将数据存入缓冲区&#xff0c;则相应的字节就自动写入文件&#xff0c;这样&#xff0c;就可在不不…

【绝对干货】kafkastream广告

Java如何入门&#xff1f; 1、建立好开发环境 首先建立好开发环境非常重要&#xff0c;工欲善其事&#xff0c;必先利其器。做任何开发&#xff0c;首先就是要把这个环境准备好&#xff0c;之后就可以去做各种尝试&#xff0c;尝试过程中就能逐渐建立信心。初学者往往在环境配…

存储映射IO(二)

mmap父子进程间通信 1. 测试代码&#xff1a; #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/wait.h>int var 100;int main(void) {int *p;pid_t pid;int fd;…

【聊透SpringMVC】java技术经理岗位职责

缓存维护方案一 如果是一读&#xff08;线程B&#xff09;一写&#xff08;线程A&#xff09;操作&#xff0c;「先操作缓存&#xff0c;再操作数据库」。流程图如下所示&#xff1a; 1.线程A发起一个写操作&#xff0c;第一步del cache 2.线程A第二步写入新数据到DB 3.线程…

【聊透SpringMVC】自学java和三大框架要多久

饿了么一面&#xff08;Java&#xff09; hashmap源码问题 HashMap底层结构 put操作讲一下 HashMap、HashMap如何保证线程安全、ConcurrentHashMap JVM有哪些回收算法&#xff0c;对应的收集器有哪些&#xff1f; jvm g1的内存模型讲一下&#xff0c;G1和CMS收集器的区别&#…

【设计思想解读开源框架】mysql官方文档中文版下载免费

01 源码分析 源码阅读&#xff0c;最核心有三点&#xff1a;技术基础强烈的求知欲耐心。 1.1 设计模式&#xff08;45设计模式&#xff1a;介绍优缺点应用实例源代码解决问题&#xff09; 1.2 Spring复习大纲&#xff1a;依赖注入IocBeans注解数据访问AOPMVC等 1.3 Spring全家…

命令新参

命令形参&#xff1a; 命令行参数是使用main()函数参数来处理的&#xff0c;其中&#xff0c;argc是指传入参数的个数&#xff0c;argv[]是一个指针数组&#xff0c;指向传递给程序的每个参数。 应当指出的是&#xff0c; argv[0]存储程序的名称&#xff0c;argv[1]是一个指向…

【金三银四】启动mysql服务器

微服务架构 ①微服务概念&#xff1a; ②Spring Cloud微服务架构&#xff1a; 海量数据处理 ①&#xff1a;经典的海量数据处理面试题 高可用架构 ①基于 Hystrix 实现高可用&#xff1a; ②限流&#xff1a; ③熔断&#xff1a; 高并发架构 ①消息队列&#xff1a; ②搜索…

函数fork vfork

一、函数fork fork函数原型&#xff1a; #include <unistd.h> pid_t fork(void); 二、程序清单 1. 测试代码&#xff1a; #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h>…

【金三银四】微软java后端社招

分布式系统特性与衡量标准 透明性&#xff1a;使用分布式系统的用户并不关心系统是怎么实现的&#xff0c;也不关心读到的数据来自哪个节点&#xff0c;对用户而言&#xff0c;分布式系统的最高境界是用户根本感知不到这是一个分布式系统 可扩展性&#xff1a;分布式系统的根…

【面试必会】java虚拟机原理

如何才可以进大厂&#xff1f; 答案其实也很简单&#xff0c;能力学历。不知道大家有没有发现&#xff0c;大厂的一些部门对于学历要求已经放低了&#xff0c;阿里的一些部门同样也招大专学历的程序员&#xff0c;当然肯定也是因为他的能力足够出色。 对于准备秋招的你来说&a…

函数exec

exec函数族 1. 执行指定目录下的程序 #include <unistd.h> int execl(const char *path, const char *arg, ...);返回值&#xff1a;若出错&#xff0c;返回-1&#xff1b;若成功&#xff0c;不返回 分析&#xff1a; path: 要执行的程序的绝对路径变参arg: 要执行的…

【面试必备】java写spark好不好

并发编程三大特性 原子性 一个操作或者多次操作&#xff0c;要么所有的操作全部都得到执行并且不会受到任何因素的干扰而中断&#xff0c;要么所有的操作都执行&#xff0c;要么都不执行。 对于基本数据类型的访问&#xff0c;读写都是原子性的【long和double可能例外】。 …

【面试必备】java面试题视频讲解

二、我们先来看看这份笔记到底有什么 1、先把kubernetes跑起来&#xff08;先跑起来创建kubernetes集群部署应用访问应用Scale应用滚动更新&#xff09; 2、重要概念 3、部署kubernetes Cluster&#xff08;安装docker安装 kubelet.kubeadm和 kubectll用kubeadm 创建cluster&a…

【面试总结】java测试工程师培训

阿里P8级架构师核心理论落地篇 再造淘宝&#xff0c;贯穿全系&#xff0c;阿里团队代码落地&#xff0c;详细每个版本迭代&#xff0c;拒绝2-3个月PPT架构师再造淘宝之咚宝-技术支撑-完整搭建DevOps再造淘宝之咚宝-统一规则-代码规范落地解析再造淘宝之咚宝搭建基础服务再造淘…

进程组的应用

一、实验1 题目&#xff1a;利用进程扇完成一个小实验。该进程扇有 1 个父进程和 3 个子进程&#xff0c;我们希望达到图 1 中的效果&#xff0c;即将进程 0 (父进程)和进程 1 设置成一组&#xff0c;假设为组 1&#xff0c;将进程 2 和 进程 3 设置成另一个组&#xff0c;假设…

【原理+实战+视频+源码】docker映射端口教程

阿里巴巴Java岗面试题分享 1.HashMap 的内部结构&#xff1f;内部原理&#xff1f;和 HashTable 的区别&#xff0c;假如发⽣了 hash 碰撞&#xff0c;如何设计能让遍历效率⾼&#xff1f; 2.讲一讲讲讲 ConcurrentHashMap吧。 3.讲一下JVM虚拟机内存结构&#xff0c;以及它…

前台进程组、后台进程组

一、前台进程组、后台进程组 cat | cat & cat | cat | cat 输出结果&#xff1a; 二、主要函数应用 1. tcgetpgrp函数原型&#xff1a; #include <unistd.h> pid_t tcgetpgrp(int fd);返回值&#xff1a;若成功&#xff0c;返回前台进程组ID, 若出错&#xff0c;…