【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能够设计自己的人…...
Redis未授权访问漏洞实战:从SSH公钥到反弹shell的5种利用方式详解
Redis未授权访问漏洞深度攻防:5种高阶利用与防御方案 Redis作为高性能键值数据库,其未授权访问漏洞长期位居企业安全风险Top 10。本文将突破常规教程框架,从攻击者视角剖析5种实战利用手法,同时提供企业级防御方案。不同于基础复现…...
逆向分析WhatsApp的GIF功能:用Frida抓取Tenor API的完整请求与响应数据
逆向工程实战:用Frida解密WhatsApp的GIF数据流 当你在WhatsApp中发送一个GIF表情时,是否好奇过这个动态图片是如何从服务器传输到你的手机上的?今天我们将深入WhatsApp客户端内部,通过动态插桩工具Frida来捕获和分析其背后的Tenor…...
计算机毕业设计springboot月子中心健康管理系统 基于SpringBoot的母婴护理中心智能管理平台 产后康复中心信息化服务系统
计算机毕业设计springboot月子中心健康管理系统7639p9(配套有源码 程序 mysql数据库 论文)本套源码可以先看具体功能演示视频领取,文末有联xi 可分享随着国家三胎政策的放开和居民生活水平的提升,现代家庭对产后护理服务的专业化、…...
Open Props:重新定义CSS自定义属性的高效设计系统
Open Props:重新定义CSS自定义属性的高效设计系统 【免费下载链接】open-props CSS custom properties to help accelerate adaptive and consistent design. 项目地址: https://gitcode.com/gh_mirrors/op/open-props 在前端开发领域,样式一致性…...
nli-distilroberta-base环境配置:Ubuntu/CentOS下Python依赖与端口映射设置
nli-distilroberta-base环境配置:Ubuntu/CentOS下Python依赖与端口映射设置 1. 项目概述 nli-distilroberta-base是一个基于DistilRoBERTa模型的自然语言推理(NLI)Web服务,专门用于判断两个句子之间的逻辑关系。这个轻量级模型保留了RoBERTa-base模型9…...
工单系统已经上线,但 IT 管理并没有真正变好
在很多企业中,引入 IT 工单系统往往被视为 IT 管理升级的重要一步。 有了统一入口、有了记录机制、有了流程流转,看起来一切都开始变得规范起来。但实际运行一段时间后,不少团队会发现: 工单确实在增加,流程也在走&…...
嵌入式开发调试与问题诊断实战指南
嵌入式工程师常见问题诊断与调试经验分享1. 典型开发场景分析1.1 开发环境差异问题"在我的开发环境运行正常"是嵌入式工程师最常遇到的困境之一。这种现象通常源于:编译器版本差异(GCC/Keil/IAR版本不一致)硬件平台差异(…...
OpenClaw飞书机器人配置指南:百川2-13B-4bits量化模型对话触发
OpenClaw飞书机器人配置指南:百川2-13B-4bits量化模型对话触发 1. 为什么选择OpenClaw飞书百川2的组合? 去年我接手了一个小团队的日报自动化项目,需要每天收集5个成员的进度更新并生成汇总报告。最初尝试用Python脚本钉钉机器人࿰…...
League-Toolkit:智能辅助驱动的英雄联盟效率提升解决方案
League-Toolkit:智能辅助驱动的英雄联盟效率提升解决方案 【免费下载链接】League-Toolkit 兴趣使然的、简单易用的英雄联盟工具集。支持战绩查询、自动秒选等功能。基于 LCU API。 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit League-Tool…...
超实用!学生党第一把吉他怎么选?9款“低弦距神器”深度测评与避坑指南!
大家好,我是深耕音乐教育与乐器选购多年的好物推荐官,常年和学生党打交道,最常被问到的问题就是:“预算有限,怎么选到好弹又耐用的吉他?” 其实对学生而言,第一把吉他无需追求高端奢华ÿ…...
