【Linux】线程同步

news2024/11/25 7:35:49

文章目录

    • 条件变量相关函数
      • 初始化条件变量-pthread_cond_init
      • 销毁条件变量-pthread_cond_destroy
      • 等待条件变量-pthread_cond_wait
      • 唤醒等待条件变量
        • pthread_cond_broadcast
        • pthread_cond_signal
    • 小例子
    • 关于等待函数的补充
    • 条件变量使用规范

条件变量相关函数

初始化条件变量-pthread_cond_init

初始化条件变量的函数叫做pthread_cond_init

#include<pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);

参数说明

cond:需要初始化的条件变量

attr:初始化条件变量的属性,一般设置为NULL

返回值说明

初始化成功返回0,失败返回错误码


注意:调用pthread_cond_init函数初始化条件变量叫做动态分配,我们还可以用静态分配的方式初始化条件变量

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

销毁条件变量-pthread_cond_destroy

销毁条件变量的函数叫做pthread_cond_destroy

#include<pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);

参数说明

cond:需要销毁的条件变量

返回值说明

销毁成功返回0,失败返回错误码


注意:使用PTHREAD_COND_INITIALIZER初始化 (静态分配)的条件变量不需要销毁


等待条件变量-pthread_cond_wait

等待条件变量满足的函数叫做pthread_cond_wait

#include<pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

参数说明

cond:需要等待的条件变量

mutex:当前线程所处临界区对应的互斥锁

返回值说明

函数调用成功返回0,失败返回错误码


唤醒等待条件变量

唤醒等待的函数有以下两个

pthread_cond_broadcast

#include<pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);

pthread_cond_signal

#include<pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);

参数说明

cond:唤醒在cond条件变量下等待的线程

返回值说明

函数调用成功返回0,失败返回错误码


区别

  • pthread_cond_signal函数用于唤醒等待队列中首个线程
  • pthread_cond_broadcast函数用于唤醒等待队列中的全部线程

小例子

下面我们用主线程创建三个新线程,让主线程控制这三个新线程, 这三个新线程创建后都在条件变量下进行等待,直到被主线程唤醒

#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>

pthread_mutex_t mtx;//互斥锁
pthread_cond_t cond;//条件变量

//主线程控制新线程
void *ctrl(void *args)
{
    std::string name = (char*)args;
    while(1)
    {
        std::cout << "master say : begin work" << std::endl;
        //pthread_cond_signal函数作用:唤醒在条件变量下等待的 一个 线程
        //哪一个呢?当前在等待队列里等待的第一个线程
        pthread_cond_signal(&cond); 
        sleep(2);
    }
}
void *work(void *args)
{
    int number = *(int*)args;
    delete (int*)args;
    while(1)
    {
        pthread_cond_wait(&cond, &mtx);//等待条件变量
        std::cout << "worker: " << number << " is working ..." << std::endl;
    }
}
#define NUM 3
int main()
{
    pthread_mutex_init(&mtx, nullptr);//初始化这把锁
    pthread_cond_init(&cond, nullptr);//初始化条件变量
    pthread_t master;
    pthread_t worker[NUM];
    pthread_create(&master, nullptr, ctrl, (void*)"boss");
    //创建NUM个线程
    for(int i = 0; i < NUM; i++)
    {
        int *number = new int(i);
        pthread_create(worker+i, nullptr, work, (void*)number);
    }
    //线程等待
    for(int i = 0; i < NUM; i++)
    {
        pthread_join(worker[i], nullptr);
    }

    pthread_join(master, nullptr);//线程等待
    
    pthread_mutex_destroy(&mtx);//释放锁
    pthread_cond_destroy(&cond);//释放条件变量
    return 0;
}

此时我们会发现唤醒这三个线程时具有明显的顺序性

根本原因是当这若干个线程启动时默认都会在该条件变量下去等待,而我们每次都唤醒的是在当前条件变量下等待的头部线程,当该线程执行完打印操作后会继续排到等待队列的尾部进行wait,所以我们能够看到一个周转的现象

image-20220826205924953


如果我们想每次唤醒都将在该条件变量下等待的所有线程进行唤醒,可以将代码中的pthread_cond_signal函数改为pthread_cond_broadcast函数

//主线程控制新线程
void *ctrl(void *args)
{
    std::string name = (char*)args;
    while(1)
    {
        std::cout << "master say : begin work" << std::endl;
        pthread_cond_broadcast(&cond);
        sleep(2);
    }
}

此时我们每一次唤醒都会将所有在该条件变量下等待的线程进行唤醒==>也就是每次都将这三个线程唤醒

image-20220826210239064


关于等待函数的补充

为什么pthread_cond_wait函数的第二个参数需要传入互斥量

1)条件等待是线程间同步的一种手段,如果只有一个线程,条件不满足,一直等下去都不会满足,所以必须要有一个线程通过某些操作,改变共享变量,使原先不满足的条件变得满足,并且友好的通知等待在条件变量上的线程

2)条件不会无缘无故的突然变得满足了,必然会牵扯到共享数据的变化,所以一定要用互斥锁来保护,没有互斥锁就无法安全的获取和修改共享数据

image-20220826211007144

3)当线程进入临界区时需要先加锁,然后判断内部资源的情况,若不满足当前线程的执行条件,则需要在该条件变量下进行等待,但此时该线程是拿着锁被挂起的,也就意味着这个锁再也不会被释放了,此时就会发生死锁问题

4)所以在调用pthread_cond_wait函数时,还需要将对应的互斥锁传入,此时当线程因为某些条件不满足需要在该条件变量下进行等待时,就会自动释放该互斥锁

5)当该线程被唤醒时,该线程会接着执行临界区内的代码,此时便要求该线程必须立马获得对应的互斥锁,因此当某一个线程被唤醒时,实际会自动获得对应的互斥锁


总结:

  • 当该线程进入等待的时候,互斥锁会自动释放,而当该线程被唤醒时,又会自动获得对应的互斥锁
  • 条件变量需要配合互斥锁使用,其中条件变量是用来完成同步的,而互斥锁是用来完成互斥的
  • pthread_cond_wait函数有两个功能,一就是让线程在特定的条件变量下等待,二就是让线程释放对应的互斥锁

我们可以不可以:当我们进入临界区上锁后,如果发现条件不满足,先解锁,然后在该条件变量下进行等待

即:

//错误的设计
pthread_mutex_lock(&mutex);
while (condition_is_false){
	pthread_mutex_unlock(&mutex);//解锁
	//解锁之后,等待之前,条件可能已经满足,信号已经发出,但是该信号可能被错过
	pthread_cond_wait(&cond);
	pthread_mutex_lock(&mutex);//再加锁
}
pthread_mutex_unlock(&mutex);//解锁

不可行!因为解锁和等待不是原子操作,调用解锁之后,在调用pthread_cond_wait函数之前,如果已经有其他线程获取到互斥量,发现此时条件满足,于是发送了信号,那么此时pthread_cond_wait函数将错过这个信号,最终可能会导致线程永远不会被唤醒,因此解锁和等待必须是一个原子操作

进入pthread_cond_wait函数后,会先判断条件变量是否等于0,若等于0则说明不满足,此时会先将对应的互斥锁解锁,直到pthread_cond_wait函数返回时再将条件变量改为1,并将对应的互斥锁加锁


条件变量使用规范

等待条件变量的代码

pthread_mutex_lock(&mutex);//加锁
while (条件为假)
	pthread_cond_wait(&cond, &mutex);//条件不满足,就一直等待
修改条件
pthread_mutex_unlock(&mutex);//解锁

唤醒等待线程的代码

pthread_mutex_lock(&mutex);//加锁
设置条件为真
pthread_cond_signal(&cond);//满足条件了,唤醒在条件变量等待队列当中的第一个线程
pthread_mutex_unlock(&mutex);//解锁

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

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

相关文章

多台linux设备之间设置免密登陆

1、首先&#xff0c;先搞一个公钥&#xff0c;如果已经有公钥了&#xff0c;请自行跳过 我这是有了&#xff0c;如果没有&#xff0c;也没关系&#xff0c;造一个就好&#xff0c;命令为&#xff1a; ssh-keygen -t rsa 一路回车就行&#xff0c;看下执行结果&#xff1a; 2、…

使用AWTK实现汽车仪表Cluster/DashBoard嵌入式开发概述

AWTK=Toolkit AnyWhere,一款国产免费开源工具,ZLG开发的开源GUI引擎,为嵌入式等系统提供图形界面开发IDE。 随着汽车技术的发展,汽车仪表盘也在快速发展,从最初的机械式到电气式,再到数字化。这次电动化、智能化又一次让汽车仪表出现了飞跃式的发展,再未来,仪表板上可…

【vsomeip】vsomeip安装与入门案例

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍vsomeip的使用。 学其所用&#xff0c;用其所学。——梁启超 欢迎来到我的博客&#xff0c;一起学习知识&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下次更新不迷路&am…

luatOS网站 lua语言学习 练习题

lua 教程跳转链接&#xff0c;练习题都来自这里 逻辑运算 检验大小&#xff08;自测题&#xff09; 题目&#xff1a;如果已知number变量n&#xff0c;那么如果需要判断n是否符合下面的条件&#xff1a; 3<n≤10 以下四行判断代码&#xff0c;正确的是&#xff1f; &a…

【C++】运算符重载(日期类的实现)

【C】运算符重载&#xff08;日期类的实现&#xff09; 前言运算符重载operator全局和类中 日期类的实现成员变量的确定构造函数拷贝构造 运算符重载部分的重载思路实现GETmonthdayoperator 的重载思路实现 -的与-的重载实现 各个比较运算符的重载实现 前置与后置实现 &#xf…

接口自动化【六】——接口关联之jsonpath提取+设置全局变量+通用封装

文章目录 前言一、jsonpath提取二、jsonpath与excel当中提取表达式结合三、类的动态属性设置四、设置全局变量&#xff08;这个模块就作为一个讲解&#xff09;五、new_handle_global_data.py六、new_handle_extract.py七、test_new_upload_image.py 文件中上传图片的代码更改八…

mybatis 在当前项目中的实际应用及自定义分页的实现

mybatis 在当前项目中的实际应用及自定义分页的实现 项目中分页代码的解耦 实现目标 实现目标&#xff0c;使用spring 提供的分页相关的类&#xff0c;避免代码中直接使用PageHelper 具体实现 创建自定义PageHelper&#xff0c;并使用spring-data-common提供的具体实现类操…

OTA升级技术概览

随着物联网技术的不断发展&#xff0c;越来越多的设备和系统需要进行远程升级以保持其安全性和功能性。OTA&#xff08;Over-the-Air&#xff09;升级技术是一种通过无线网络远程升级固件或软件的方法&#xff0c;已经成为现代工业、智能家居、汽车等领域中广泛应用的技术。本文…

欧拉公式——最令人着迷的公式之一

欧拉公式是数学里最令人着迷的公式之一&#xff0c;它将数学里最重要的几个常数联系到了一起&#xff1a;两个超越数&#xff1a;自然对数的底e&#xff0c;圆周率π&#xff1b;两个单位&#xff1a;虚数单位i和自然数的单位1&#xff0c;以及数学里常见的0。 ​而且它对数学领…

generate 和 summary 配合——解析 bingchat 逻辑

generate 和 summary 配合——解析 bingchat 逻辑 new bing 微软作为 openai 公司背后的大股东&#xff0c;多年投入一朝开花结果&#xff0c;当然要把 ChatGPT 技术融入到自己的核心产品中&#xff0c;提升整体生产力。微软的第一个措施&#xff0c;就是在必应搜索引擎 bing…

chatgpt赋能Python-python_span_抓取

介绍 随着互联网的不断发展&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;已成为所有网站主人必须面对的问题。在SEO中&#xff0c;抓取是一个非常重要的环节&#xff0c;也是一个关键性的步骤&#xff0c;它直接影响到网站的排名。 在Python编程中&#xff0c;有很多…

Mac上安装多个版本的MySQL

文章目录 准备工作先确定自己机器是多少位的下载包 具体步骤1. 先安装低版本的MySQL2. 清理完后&#xff0c;再安装高版本的MySQL3. 将低版本的文件夹移回 /usr/local4. 切换版本5. 验证 扩展清理命令其他信息 准备工作 先确定自己机器是多少位的 uname -a输出X86_64&#xf…

【Error】Python3.7 No module named ‘_sqlite3‘ 解决方案

场景&#xff1a;docker容器运行keybert时出现错误 No module named ‘_sqlite3‘&#xff0c;是容器环境没有sqlite的库&#xff0c;如下图所示&#xff1a; 本机是能够正常导入sqlite3的&#xff0c;虚拟环境conda下也有该库。 python3.8版本的不可用于python3.7中&#xff0…

【LeetCode】169. 多数元素

169. 多数元素&#xff08;简单&#xff09; 方法一&#xff1a;sort排序&#xff0c;时间复杂度为O(nlogn) 思路 我自己的写法用了最简单的方法&#xff0c;首先使用 sort() 对数组元素按照从小到大进行排序&#xff0c;然后依次遍历每个元素&#xff0c;如果该元素的出现次…

CompletableFuture 异步编排如何使用?

概述&#xff1a; 在做提交订单功能时&#xff0c;我们需要处理的事务很多&#xff0c;如&#xff1a;修改库存、计算优惠促销信息、会员积分加减、线上支付、金额计算、生成产品订单、生成财务信息、删除购物车等等。如果这些功能全部串行化处理&#xff0c;会发费很长的时间…

【盘点】界面控件DevExpress WPF的几大应用程序主题

DevExpress WPF控件包含了50个应用程序主题和40个调色板&#xff0c;用户可以在发布应用程序是指定主题&#xff0c;或允许最终用户动态修改WPF应用程序的外观和样式&#xff0c;其中主题带有调色板&#xff0c;可以进一步个性化您的UI&#xff01; PS&#xff1a;DevExpress …

oracle WAITED TOO LONG FOR A ROW CACHE ENQUEUE LOCK问题分析

服务概述 财务系统出现业务卡顿&#xff0c;数据库实例2遇到>>> WAITED TOO LONG FOR A ROW CACHE ENQUEUE LOCK! <<<错误导致业务HANG住。 对此HANG的原因进行分析&#xff1a; 故障发生时&#xff0c;未有主机监控数据&#xff0c;从可以获取的数据库A…

c++ 11标准模板(STL) std::map(三)

定义于头文件<map> template< class Key, class T, class Compare std::less<Key>, class Allocator std::allocator<std::pair<const Key, T> > > class map;(1)namespace pmr { template <class Key, class T, clas…

使用Linkage Mapper工进行物种分布建模的步骤详解(含实际案例分析)

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Linkage Mapper解密数字世界链接 文章目录 引言:一、介绍二、数据准备2.1 物种分布数据获取2.2 环境变量数据获取2.3 数据预处理

【拼多多API 开发系列】百亿补贴商品详情接口,代码封装

为了进行电商平台 PDD 的API开发&#xff0c;首先我们需要做下面几件事情。 1&#xff09;开发者注册一个账号 2&#xff09;然后为每个 PDD 应用注册一个应用程序键&#xff08;App Key) 。 3&#xff09;下载 PDD API的SDK并掌握基本的API基础知识和调用 4&#xff09;利用SD…