Linux之线程同步

目录

一、问题引入

二、实现线程同步的方案——条件变量

1、常用接口:

2、使用示例


一、问题引入

我们再次看看上次讲到的多线程抢票的代码:这次我们让一个线程抢完票之后不去做任何事。

#include <iostream>
#include <unistd.h>
#include <cstring>
#include <time.h>
#include <pthread.h>using namespace std;
#define THREAD_NUM 5class threaddata
{
public:threaddata(const string &s, pthread_mutex_t *m): name(s), mtx(m){}public:string name;pthread_mutex_t *mtx;
};int ticket = 100;void *getticket(void *arg)
{threaddata *td = (threaddata *)arg;while (true){pthread_mutex_lock(td->mtx); if (ticket > 0)              {usleep(rand() % 10000);cout << td->name << ":"<< " " << ticket << endl;ticket--;pthread_mutex_unlock(td->mtx); }else{pthread_mutex_unlock(td->mtx); break;}}delete td;return nullptr;
}int main()
{pthread_mutex_t mtx;pthread_mutex_init(&mtx, nullptr);pthread_t t[THREAD_NUM];for (int i = 0; i < THREAD_NUM; i++){string name = "thread ";name += to_string(i + 1);threaddata *td = new threaddata(name, &mtx);pthread_create(t + i, nullptr, getticket, (void *)td);}for (int i = 0; i < THREAD_NUM; i++)pthread_join(t[i], nullptr);pthread_mutex_destroy(&mtx);return 0;
}

运行结果:

我们这就发现了一个问题,对于抢票系统,我们看到的是只有一个线程5在一直连续抢票,没有其他的线程。这很不合理。

这是因为如果个别线程的竞争力特别强,每次都能够申请到锁,但申请到锁之后什么也不做,所以在我们看来这个线程就一直在申请锁和释放锁,那么它就可以一直抢票。这就可能导致其他线程长时间竞争不到锁,造成了其他线程的饥饿问题(无法抢票)。虽然,你是拿到锁后再去访问临界资源,并且最后还释放了锁,由于竞争能力太强,可以一直拿到锁,这没有错,但这不合理。

为了解决这个问题,我们增加一个限制:当一个线程释放锁后,这个线程不能立马再次申请锁,该线程必须排到这个锁的资源等待队列的最后。这样,我们就有了线程同步:我们在保证数据安全的情况下让这些线程按照一定的顺序进行临界资源的访问,这就是线程同步。

竞态条件:因为时序问题,而导致程序异常,我们称为竞态条件。

二、实现线程同步的方案——条件变量

当一个线程互斥地访问某个变量时,它可能发现在其他线程改变状态之前,它什么也做不了

例如一个线程访问队列时,发现队列为空,它只能等待,直到其他线程将一个节点添加到队列中。这种情况就需要用到条件变量。

1、常用接口:

1、条件变量的定义和初始化

​
NAMEpthread_cond_destroy, pthread_cond_init - destroy and initialize condition variablesSYNOPSIS#include <pthread.h>//销毁int pthread_cond_destroy(pthread_cond_t *cond);//初始化int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);//全局和静态变量初始化pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

2、线程等待临界资源: 

pthread_cond_wait 功能:一就是让线程在特定的条件变量下等待,二就是让线程在等待时释放对应的互斥锁。当线程被唤醒时,该函数会帮助我们线程获取锁。

#include <pthread.h>
//特定时间阻塞等待
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
//等待
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

 3、唤醒线程去访问临界资源

#include <pthread.h>
// 唤醒所有等待的线程
int pthread_cond_broadcast(pthread_cond_t *cond);// 唤醒一个线程
int pthread_cond_signal(pthread_cond_t *cond);

注:1、条件变量通常需要配合互斥锁一起使用。

2、条件变量的使用:一个线程等待条件变量的条件成立而被挂起;另一个线程使条件成立后唤醒等待的线程。

3、等待的时候往往是在临界区内等待的。(加锁与解锁之间的区域进行等待)

4、线程被唤醒,是在之前进行等待的地方被唤醒。

2、使用示例

有了线程同步,我们就可以改进我们之前的抢票系统的代码:

#include <iostream>
#include <string>
#include <time.h>
#include <unistd.h>
#include <pthread.h>using namespace std;
#define THREADNUM 3
typedef void *(*func)(void *argc);class threaddata
{
public:threaddata(pthread_mutex_t *mtx, pthread_cond_t *cond, const string &name): mtx_(mtx), cond_(cond), name_(name){}public:pthread_mutex_t *mtx_;pthread_cond_t *cond_;string name_;
};int ticket = 100;void *getticket(void *arg)
{threaddata *td = (threaddata *)arg;while (true){pthread_mutex_lock(td->mtx_);pthread_cond_wait(td->cond_, td->mtx_); // 在加锁和解锁之间进行等待if (ticket > 0){usleep(rand() % 10000);cout << td->name_ << ":"<< " " << ticket << endl;ticket--;pthread_mutex_unlock(td->mtx_);}else{pthread_mutex_unlock(td->mtx_);break;}}delete td;return nullptr;
}void *fun1(void *arg)
{threaddata *td = (threaddata *)arg;while (true){getticket((void *)td);sleep(1);}
}void *fun2(void *arg)
{threaddata *td = (threaddata *)arg;while (true){getticket((void *)td);sleep(1);}
}void *fun3(void *arg)
{threaddata *td = (threaddata *)arg;while (true){getticket((void *)td);sleep(1);}
}int main()
{srand((unsigned long)time(nullptr) ^ getpid() ^ 433);pthread_mutex_t mtx;pthread_cond_t cond;pthread_mutex_init(&mtx, nullptr);pthread_cond_init(&cond, nullptr);pthread_t t[THREADNUM];func fun[THREADNUM] = {fun1, fun2, fun3};for (int i = 0; i < THREADNUM; i++){string name = "thread ";name += to_string(i + 1);threaddata *td = new threaddata(&mtx, &cond, name);pthread_create(t + i, nullptr, fun[i], (void *)td);}sleep(5);while (true){pthread_cond_signal(&cond);sleep(1);}for (int i = 0; i < THREADNUM; i++)pthread_join(t[i], nullptr);pthread_mutex_destroy(&mtx);pthread_cond_destroy(&cond);return 0;
}

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

此时我们会发现唤醒这三个线程时具有明显的顺序性,因为这些线程启动时默认都会在该条件变量下去等待,而我们每次都唤醒的是在当前条件变量下等待的头部线程,当该线程执行完代码后会继续排到等待队列的尾部进行等待。

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

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

相关文章

【NLP笔记】Transformer

文章目录 基本架构EmbeddingEncoderself-attentionMulti-Attention残差连接LayerNorm DecoderMask&Cross Attention线性层&softmax损失函数 论文链接&#xff1a; Attention Is All You Need 参考文章&#xff1a; 【NLP】《Attention Is All You Need》的阅读笔记 一…

【C语言】结构体类型名、变量名以及typedef

文章目录 分类判断结构体成员的使用typedef 分类判断 struct tag {char m;int i; }p;假设定义了上面这一个结构体&#xff0c;tag 就是类型名&#xff0c; p 就是变量名&#xff0c; m 和 i 就是结构体成员列表。 可以这么记&#xff0c;括号前面的是类型名&#xff0c;括号后…

内存条@电脑支持的最大内存@升级内存硬件

文章目录 电脑支持的最大内存规格cpu官网查看支持的规格命令行查看脚本化 DDR内存LPDDR内存内存升级扩展&#x1f47a;插槽检查板载内存SPD内存厂商其他 内存参数&#x1f47a;性能指标使用软件查看更多内存相关的软件工具 电脑支持的最大内存规格 确认电脑最大支持内存大小和频…

在Ubuntu20.04(原为cuda12.0, gcc9.几版本和g++9.几版本)下先安装cuda9.0后再配置gcc-5环境

因为自己对Linux相关操作不是很熟悉&#xff0c;所以因为之前的代码报错之后决定要安cuda9.0&#xff0c;于是先安装了cuda9.0。里面用到的一些链接&#xff0c;链接文件夹时直接去copy它的路径&#xff0c;就不那么容易错了。 今天运行程序之后发现gcc环境不太匹配cuda9.0&am…

图解CodeWhisperer的安装使用

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! ​ 目录 &#x1f4d8; CodeWhisperer简介 &#…

原生html vue3使用element plus 的树tree上移下移案例源码

上效果 html源码 <!DOCTYPE html> <html lang"en"> <!-- * Name: mallSalesReports.html * Description: * Author Lani * date 2024-02-28 18:32:36 --> <head><meta charset"UTF-8"><meta name"viewport" …

[uni-app] uni.createAnimation动画在APP端无效问题记录

文章目录 uni.createAnimation动画描述动画代码templatejs部分 问题原因改进方案template js部分改动git 改进截图 uni.createAnimation 动画描述 实现一个以左上角为锚点,以Z轴做平面抬起及落下的动画效果 动画代码 template <image v-if"showHot(item.cname)&quo…

wayland(xdg_wm_base) + egl + opengles 使用 Assimp 加载带光照信息的材质文件Mtl 实现光照贴图的最简实例(十七)

文章目录 前言一、3d 立方体 model 属性相关文件1. cube1.obj2. cube1.Mtl3. 纹理图片 cordeBouee4.jpg二、实现光照贴图的效果1. 依赖库和头文件1.1 assimp1.2 stb_image.h2. egl_wayland_obj_cube1.cpp3. Matrix.h 和 Matrix.cpp4. xdg-shell-client-protocol.h 和 xdg-shell…

GitHub gpg体验

文档 实践 生成新 GPG 密钥 gpg --full-generate-key查看本地GPG列表 gpg --list-keys关联GPG公钥与Github账户 gpg --armor --export {key_id}GPG私钥对Git commit进行签名 git config --local user.signingkey {key_id} # git config --global user.signingkey {key_id} git…

ASPICE规范之系统追溯矩阵

系统追溯矩阵的需求来自 ISO26262 举例在描述系统追溯矩阵时&#xff1a;客户需求->系统需求&#xff1b;系统需求->客户需求&#xff1b;系统需求->软件需求&#xff1b;系统需求->硬件需求

(简单成功)Mac:命令设置别名

案例&#xff1a;给"ls -l"命令&#xff0c;设置别名通过”ll“快速访问 1、在项目根目录底下查看有无.bash_profile文件&#xff0c;注意这个是个隐藏文件&#xff0c;需要使用ls -a命令查看&#xff1a; 没有.bash_profile新建一个文件&#xff0c; 在最后添加一行…

.NET 异步编程(异步方法、异步委托、CancellationToken、WhenAll、yield)

文章目录 异步方法异步委托async方法缺点CancellationTokenWhenAllyield 异步方法 “异步方法”&#xff1a;用async关键字修饰的方法 异步方法的返回值一般是Task<T>&#xff0c;T是真正的返回值类型&#xff0c;Task<int>。惯例&#xff1a;异步方法名字以 Asy…

Java城管智慧执法管理系统源码带APP

目录 一、系统概述 二、系统开发环境 三、功能模块 四、应用价值 1、提升案件办理效率 2、提升监管效能 3、提升行政执法水平 4、推进行政执法创新 一、系统概述 智慧城管系统是一个基于现代信息技术手段的综合管理平台&#xff0c;旨在通过强化信息获取自动化、监督管…

UE5拷贝复制快捷键修改Ctrl+w

UE5默认修改了原来的Ctrl w的快捷键方式&#xff0c;改成Ctrl D 非常不习惯 其实可以在编辑器中进行修改快捷键的 位置在 Editor Preferences &#xff0c;搜索 Duplicate&#xff0c; 在其中的command selection中&#xff0c;修改 按键为Ctrl w 如图所示&#xff1b; …

js指定浏览器打开页面

文章目录 前言Js错误示例Node.js实现小结 前言 今天在过需求的时候&#xff0c;碰到一个问题&#xff0c;A系统要将B系统的页面嵌入进来并进行展示。但是A系统只支持google浏览器&#xff0c;而B系统里面有几个菜单是必须运行在IE浏览器中的&#xff0c;因此该怎么实现呢&…

MT6825编码器在STM32中的使用

文章目录 1、PWM 绝对值位置读取功能1.1 DataSheet说明1.2 硬件支持1.3 Cubemax配置及使用1.4 项目代码实现1.5 效果验证1.6 注意事项 2、SPI 绝对值位置读取功能2.2 硬件支持2.3 Cubemax配置及使用2.4 项目代码实现 1、PWM 绝对值位置读取功能 1.1 DataSheet说明 1.2 硬件支持…

计算机二级(Python)真题讲解每日一题:《方菱形》

描述‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‮‬ 请写代码替换横线&#xff0…

uniapp 云开发省钱之调整函数执行内存大小

我这个5块钱一个月的服务空间配置&#xff1a; 现在还只有少量的用户和自己测试之用&#xff0c;目前消耗的情况&#xff1a; 云函数的使用量还是挺高的&#xff0c;目前还是正好能覆盖一个月的使用量&#xff0c;等用户量上来肯定是不行的&#xff0c;所以得想想办法压榨一下云…

2024年度最佳大型语言模型(LLMs)汇总

大型语言模型(LLMs)是人工智能文本处理的主要类型&#xff0c;也现在最流行的人工智能应用形态。ChatGPT是迄今为止最著名的使用LLM的工具&#xff0c;它由OpenAI的GPT模型的特别调整版本提供动力。但还有许多其他聊天机器人和文本生成器&#xff0c;包括从Google Bard和Anthro…

Basic RNN

文章目录 回顾RNNRNN CellRNNCell的使用RNN的使用 RNN例子使用RNN Cell实现使用RNN实现 嵌入层 Embedding独热向量的缺点Embedding LSTMGRU(门控循环单元)练习 回顾 DNN&#xff08;全连接&#xff09;&#xff1a;和CNN相比&#xff0c;拥有巨大的参数量&#xff0c;CNN权重共…