【Linix-Day12-线程同步和线程安全】
线程同步 和 线程安全
线程同步
除了信号量和互斥锁(互斥锁和条件变量上次介绍过),还有两种方式同步
1.读写锁
当同时对一块内存读写时,会出现下列问题,故而引入读写锁

接口介绍:
1.int pthread_rwlock_init(pthread_rwlock_t *rwlock, pthread_rwlockattr_t *attr);
函数功能:初始化读写锁
rwlock :传入定义的读写锁地址
attr : 读写锁的属性,一般默认为 NULL
返回值:如果成功,函数应返回零
2.int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
函数功能:加读锁,其他线程无法写入
rwlock :传入定义的读写锁地址
3.int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
函数功能:加写锁,其他线程无法写入,读取
rwlock :传入定义的读写锁地址
4.int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
函数功能:解锁,允许读和写
rwlock :传入定义的读写锁地址
5.int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
函数功能:销毁读写锁
rwlock :传入定义的读写锁地址
测试代码:
main.c
include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>pthread_rwlock_t rwlock;void *fun1(void *arg)
{for (int i = 0; i < 30; ++i){pthread_rwlock_rdlock(&rwlock);printf("fun1 read start\n");sleep(1);printf("fun1 read end\n");pthread_rwlock_unlock(&rwlock);sleep(1);}
}
void *fun2(void *arg)
{for (int i = 0; i < 10; ++i){pthread_rwlock_rdlock(&rwlock);printf("fun2 read start\n");sleep(3);printf("fun2 read end\n");pthread_rwlock_unlock(&rwlock);sleep(1);}
}
void *fun3(void *arg)
{for (int i = 0; i < 10; ++i){pthread_rwlock_wrlock(&rwlock);printf("fun3 write start\n");sleep(3);printf("fun3 write end\n");pthread_rwlock_unlock(&rwlock);sleep(1);}
}
int main()
{pthread_t id[3];pthread_rwlock_init(&rwlock, NULL);pthread_create(&id[0], NULL, fun1, NULL);pthread_create(&id[0], NULL, fun2, NULL);pthread_create(&id[0], NULL, fun3, NULL);for (int i = 0; i < 3; ++i){pthread_join(id[i], NULL);}pthread_rwlock_destroy(&rwlock);exit(0);
}
运行结果图:

2.条件变量
条件变量提供了一种线程间的通知机制:当某个共享数据达到某个值的时候,唤醒等待
这个共享数据的线程。
接口介绍:
1. int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);
函数功能:初始化条件变量
cond:定义的条件变量的地址
attr:条件变量的属性,一般默认为 NULL
2. int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
函数功能:进入条件变量等待队列
cond:定义的条件变量的地址
mutex:定义的互斥锁的地址
3. int pthread_cond_signal(pthread_cond_t *cond);
函数功能: 唤醒单个线程
cond:定义的条件变量的地址
4. int pthread_cond_broadcast(pthread_cond_t *cond);
函数功能:唤醒所有等待的线程
cond:定义的条件变量的地址
5.int pthread_cond_destroy(pthread_cond_t *cond);
函数功能:摧毁条件变量
cond:定义的条件变量的地址
测试代码
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
pthread_cond_t cond;
pthread_mutex_t mutex;void *fun1(void *arg)
{char *s = (char *)arg;while (1){// 阻塞,被唤醒pthread_mutex_lock(&mutex);pthread_cond_wait(&cond, &mutex);pthread_mutex_unlock(&mutex);printf("fun1 read:%s\n", s);if (strncmp(s, "end", 3) == 0){break;}}
}
void *fun2(void *arg)
{char *s = (char *)arg;while (1){// 阻塞,被唤醒pthread_mutex_lock(&mutex);pthread_cond_wait(&cond, &mutex);pthread_mutex_unlock(&mutex);printf("fun2 read:%s\n", s);if (strncmp(s,"end",3) == 0){break;}}
}int main()
{pthread_cond_init(&cond, NULL);pthread_mutex_init(&mutex, NULL);pthread_t id[2];char buff[256]={0};pthread_create(&id[0], NULL, fun1, (void*)buff);pthread_create(&id[1], NULL, fun2, (void*)buff);while(1){printf("input: ");fflush(stdout);fgets(buff,255,stdin);printf("\n");if(strncmp(buff,"end",3) == 0){pthread_cond_broadcast(&cond);break;}else{pthread_cond_signal(&cond);}sleep(1);}for(int i=0;i<2;++i){pthread_join(id[i],NULL);}pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);exit(0);
}
运行结果图

线程安全
在多线程运行的时候,不论线程的调度顺序怎样,最终的结果都是一样的、正确的。那么就说这些线程是安全的。
要保证线程安全需要做到:
1)对线程同步,保证同一时刻只有一个线程访问临界资源。
2)在多线程中使用线程安全的函数(可重入函数),所谓线程安全的函数指的是:如果一个函数能被多个线程同时调用且不发生竟态条件,则我们程它是线程安全的。
下面展示使用不安全的线程函数举例
实现在两个线程中分别打印 char buff[] = “a b c d e f g h i”; char buff[] = “1 2 3 4 5 6 7 8 9”;两个数组
测试代码
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>void *PthreadFun(void *arg)
{char buff[] = "a b c d e f g h i";char *p = strtok(buff, " ");while (p != NULL){printf("fun:: %c\n", *p);p = strtok(NULL, " ");sleep(1);}
}int main()
{pthread_t id;int res = pthread_create(&id, NULL, PthreadFun, NULL);assert(res == 0);char buff[] = "1 2 3 4 5 6 7 8 9";char *p = strtok(buff, " ");while (p != NULL){printf("main:: %c\n", *p);p = strtok(NULL, " ");sleep(1);}exit(0);
}
运行结果

这是因为strtok()是线程不安全函数,内部实现使用了全局变量或静态变量,所以不能同时处理不同的字符串。
解决方法,使用线程安全版的 strtok_r();

char *strtok_r(char *str, const char *delim, char **saveptr);
str : 数组
delim: 分隔符
saveptr:是指向char*变量的指针,用来维护连续解析相同字符串的调用
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>void *PthreadFun(void *arg)
{char *q = NULL;char buff[] = "a b c d e f g h i";char *p = strtok_r(buff, " ",&q);while (p != NULL){printf("fun:: %c\n", *p);p = strtok_r(NULL, " ",&q);sleep(1);}
}int main()
{pthread_t id;int res = pthread_create(&id, NULL, PthreadFun, NULL);assert(res == 0);char *q = NULL;char buff[] = "1 2 3 4 5 6 7 8 9";char *p = strtok_r(buff, " ",&q);while (p != NULL){printf("main:: %c\n", *p);p = strtok_r(NULL, " ",&q);sleep(1);}exit(0);
}
运行结果

相关文章:
【Linix-Day12-线程同步和线程安全】
线程同步 和 线程安全 线程同步 除了信号量和互斥锁(互斥锁和条件变量上次介绍过),还有两种方式同步 1.读写锁 当同时对一块内存读写时,会出现下列问题,故而引入读写锁 接口介绍: 1.int pthread_rwloc…...
C++中使用嵌套循环遍历多维数组
C中使用嵌套循环遍历多维数组 一维数组:数组元素可以看做是一行数据。 二维数组:更像是一个表格,既有行数据又有列数据。 C没有提供二维数组类型,但用户可以创建每个元素本身都是数组的数组。例如,假设要存储 5 个城…...
linux入门---命名管道
如何创建命名管道 使用mkfifo函数就可以在程序里面创建管道文件,该函数的声明如下: 该函数需要两个参数,第一个参数表示要在哪个路径下创建管道文件并且这个路径得待上管道文件的名字,因为每个文件都有对应的权限,所…...
SpringBoot2.0入门(详细文档)
文章目录 Springboot是什么Springboot2.x依赖环境和版本新特性说明为什么学习Springboot从springboot优点来看从未来发展的趋势来看 开发环境Spring Boot开发环境搭建和项目启动jdk 的配置Spring Boot 工程的构建maven配置IDEA 快速构建maven 创建工程常用注解 完整代码 Spring…...
Aztec的隐私抽象:在尊重EVM合约开发习惯的情况下实现智能合约隐私
1. 引言 Aztec的架构,不同于当前“通过EVM兼容执行环境”所实现的区块链水平扩容趋势。Aztec内部笑称其构建的为首个非zkEVM协议。 Aztec专注于实现: 成为理解和需要智能合约隐私的开发者的终极解决方案。 Aztec为开发者提供构建隐私优先app所需的网…...
【Vue】详细介绍Vue项目的目录结构及各个核心文件的示例代码
Vue.js并没有严格的文件和目录结构要求,但一般情况下,我们的Vue项目目录结构如下: ├── node_modules/ # 项目依赖的 node 模块 ├── public/ # 公共资源目录 │ ├── favicon.ico # 网页图标 │ └──…...
【人大金仓】迁移MySql数据库到人大金仓,出现sys_config表重复
需要迁移的数据库中有张表名称为sys_config,查询的时候查询结果不符合我们的预期,经咨询金仓售后人员后得知和系统表重名… 解决问题方法如下: alter database [数据库名] set search_path to "$user", [模式名,(可选&…...
linux内核进程间通信IPC----消息队列
消息队列:提供一种从一个进程向另一个进程发送一个数据块的方法。与FIFO相比,消息队列的优势在于,它独立于发送和接收进程而存在。 1.链表式结构组织,存放于内核。 2.通过队列标识来引用。 3.通过一个数据类型来索引指定的数据。 …...
PHP实现微信小程序状态检测(违规、暂停服务、维护中、正在修复)
实现原理 进入那些状态不正常的小程序会被重定向至一个Url,使用抓包软件抓取这个Url,剔除不必要参数,使用cURl函数请求网页获得HTML内容,根据内容解析出当前APPID的小程序的状态。 代码 <?php// 编码header(Content-type:ap…...
ubuntu在线直接升级
前几天VMware上安装了ubuntu,当时的内核版本支持(ipguard,加密软件),后来ubuntu自动升级了linux内核,导致加入软件不支持,无法访问加密文件了。后来加密软件商更新了软件,但还是赶不上linux内核更新速度,还…...
学习笔记:卸载nav2 navigation2导航
nav2二进制文件安装 nav2导航安装方式分为二进制文件安装和源码方式安装,如果想用最快的方式跑通代码,推荐二进制安装,不用编译,没有缺少依赖编译失败的烦恼, 安装命令: sudo apt install ros-$ROS_DISTR…...
觉非科技数据闭环系列 | BEV感知研发实践
随着自动驾驶迈向量产场景,“BEV感知数据闭环”已成为新一代自动驾驶量产系统的核心架构。数据成为了至关重要的技术驱动力,发挥数据闭环的飞轮效应或将成为下半场从1到N的胜负关键。 觉非科技在此方面已进行了大量的研究工作,并在实际量产项…...
程序员情绪把控
文章目录 建议情绪 建议 保持稳定的情绪在工作中非常重要,以下是一些建议: 自我意识:保持对自己情绪的觉察和理解,了解自己的情绪状态和触发情绪的因素。通过自我反省和观察,你可以更好地管理和调节情绪。 健康生活方…...
弱监督目标检测:ALWOD: Active Learning for Weakly-Supervised Object Detection
论文作者:Yuting Wang,Velibor Ilic,Jiatong Li,Branislav Kisacanin,Vladimir Pavlovic 作者单位:Rutgers University;The Institute for Artificial Intelligence Research and Development of Serbia;Nvidia Corporation 论文链接:http:…...
驱动开发 day3
总结:自动创建设备节点udev的流程 1.如何创建节点 手动创建:mknod 地址 设备文件类型 主设备号 次设备号(0 - 255) 自动创建:devfs (创建节点的逻辑在内核 ---> 2.4版本以前使用) udev (创建节点的逻辑在应用层) mdev (轻量级的udev) 2.…...
许可license分析 第一章
许可分析是指对软件许可证进行详细的分析和评估,以了解组织内部对软件许可的需求和使用情况。通过许可分析,可以帮助组织更好地管理和优化软件许可证的使用。以下是一些可能的许可分析方法和步骤: 收集许可证信息:首先,…...
Goby 漏洞发布|管家婆订货易在线商城 SelectImage.aspx 文件上传漏洞
漏洞名称:管家婆订货易在线商城 SelectImage.aspx 文件上传漏洞 English Name: GJP SelectImage.aspx file upload vulnerability CVSS core: 9.8 影响资产数:2617 漏洞描述: 任我行率先针对中小企业推出了管家婆进销存、财务…...
Android屏幕录制
这里使用Java语言编写实现,完整代码如下: 文件 AndroidMainfest.xml 的主要配置 <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.com/apk/res/android"package"…...
实在智能牵手埃林哲,“TARS-RPA-Agent+云时通”双剑合璧共推企业数字化转型
近日,《数字中国建设整体布局规划》进一步明确了数字化发展的方向和节奏,对企业数字化建设提出了新要求。回看过去几十年,信息化建设如火如荼,各类IT系统如雨后春笋般涌现,系统的自动化操作及系统间数据交互共享等需求…...
拥有这个中文版CustomGPT,你也能定制自己的AI问答机器人
人工智能技术的快速发展为各行各业带来了前所未有的机会,其中之一就是定制化的问答机器人。这些机器人可以用于客户支持、知识管理、虚拟助手等多个领域,帮助企业提高效率,提供更好的用户体验。很多人可能都知道通过CustomGPT能够设计自己的人…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
