线程安全——使用线程安全函数,多线程中执行fork引发的问题及如何解决

目录

一、引例

二、线程安全

三、多线程中执行fork

3.1 多线程中某个线程调用 fork(),子进程会有和父进程相同数量的线程吗?

3.2 父进程被加锁的互斥锁 fork 后在子进程中是否已经加锁


一、引例

在主线程和函数线程中进行语句分割并输出。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>

void* thread_fun(void* arg)
{
	char buff[128]={"a b c d e f g h w q"};
	char* s=strtok(buff," ");
	while(s!=NULL)
	{
		printf("thread:s=%s\n",s);
		sleep(1);
		s=strtok(NULL," ");
	}
}

int main()
{
	pthread_t id;
	pthread_create(&id,NULL,thread_fun,NULL);
	char str[128]={"1 2 3 4 5 6 7 8 9 10"};
	char* s=strtok(str," ");
	while(s!=NULL)
	{
		printf("main:%s\n",s);
		sleep(1);
		s=strtok(NULL," ");
	}
	pthread_join(id,NULL);
	exit(0);
}

因为strtok函数不是线程安全的,因为它使用了静态变量或者全局变量。

只要使用全局变量或者静态变量的函数,在多线程中都不能使用。这些函数都不是线程安全的。

不可重入:当程序被多个线程反复调用,产生的结果会出错。

strtok_r函数是线程安全的

更改后代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>

void* thread_fun(void* arg)
{
	char buff[128]={"a b c d e f g h w q"};
	char* ptr=NULL;
	char* s=strtok_r(buff," ",&ptr);
	while(s!=NULL)
	{
		printf("thread:s=%s\n",s);
		sleep(1);
		s=strtok_r(NULL," ",&ptr);
	}
}

int main()
{
	pthread_t id;
	pthread_create(&id,NULL,thread_fun,NULL);
	char str[128]={"1 2 3 4 5 6 7 8 9 10"};
	char* ptr=NULL;
	char* s=strtok_r(str," ",&ptr);
	while(s!=NULL)
	{
		printf("main:%s\n",s);
		sleep(1);
		s=strtok_r(NULL," ",&ptr);
	}
	pthread_join(id,NULL);
	exit(0);
}

 

二、线程安全

 线程安全即就是在多线程运行的时候,不论线程的调度顺序怎样,最终的结果都是一样的、正确的。那么就说这些线程是安全的。
要保证线程安全需要做到:
1)对线程同步,保证同一时刻只有一个线程访问临界资源.

2)在多线程中使用线程安全的函数(可重入函数)

所谓线程安全的函数指的是:如果一个函数能被多个线程同时调用且不发生竟态条件,则我们称它是线程安全的。

三、多线程中执行fork

3.1 多线程中某个线程调用 fork(),子进程会有和父进程相同数量的线程吗?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>

void* fun(void* arg)
{	
	for(int i=0;i<5;i++)
	{
		printf("fun run pid=%d\n",getpid());
		sleep(1);
	}
}

int main()
{
	pthread_t id;
	pthread_create(&id,NULL,fun,NULL);

	fork();
	for(int i=0;i<5;i++)
	{
		printf("main run pid=%d\n",getpid());
		sleep(1);
	}
}

结论:fork()以后,不管父进程有多少条执行路径,子进程只有一条执行路径,这条路径就是fork所在的那条执行路径;


3.2 父进程被加锁的互斥锁 fork 后在子进程中是否已经加锁

代码测试:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include <wait.h>
pthread_mutex_t mutex;
void* fun(void* arg)
{
	pthread_mutex_lock(&mutex);
	printf("fun lock!\n");
	sleep(5);
	pthread_mutex_unlock(&mutex);
	printf("fun unlock!\n");	
}

int main()
{
	pthread_t id;
	pthread_mutex_init(&mutex,NULL);
	pthread_create(&id,NULL,fun,NULL);

	sleep(1);//保证函数线程一定结束
	pid_t pid=fork();
	if(pid==-1)
	{
		exit(1);
	}
	if(pid==0)
	{
		printf("child lock start!\n");
		pthread_mutex_lock(&mutex);
		printf("child lock success!\n");
		pthread_mutex_unlock(&mutex);
		exit(0);
	}
	wait(NULL);
	pthread_join(id,NULL);
	printf("main over!\n");
	exit(0);
}

运行结果:(阻塞)

原因如下:

其实就是:fork之后锁的状态也一并被复制了.

但是因为多进程并发运行,你也不知道某一刻锁的状态到底是什么;

也就是锁的状态在子进程中是不清晰的;也就是子进程中锁的状态你也不清楚,那么我们怎么在子进程中使用锁呢?虽然你可以直接解锁,但是这么做意义就不对了,如果本来是在保护资源,你一来就解锁,那么程序就出现问题了

结论:
父进程有锁,子进程也被复制了锁;锁的状态取决于fork的那一刻父进程的锁的状态,也就是说锁的状态也会被复制进去子进程;

如何解决上述问题?

延迟fork的复制(有人用锁的时候等一等,没人用锁的时候再fork)

没有人用锁的时候我们再去fork;那么如何判断有没有人用锁呢?我们去加锁一下,如果没有成功,就是有人用锁.如果加锁成功,就是没有人用锁,这个时候再去fork;

而这个方法(在fork前后去加锁),它是有一个线程的方法可以完成的:pthread_atfork;

int pthread_atfork(void(*prepare)(void),void(*parent)(void),void(*child)(void));

三个参数:每个参数都是一个函数指针;指针指向参数为void,返回值也为void的函数;

代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include <wait.h>
pthread_mutex_t mutex;
//准备两个函数
void prepare(void)
{
	pthread_mutex_lock(&mutex);
}

void after(void)
{
	pthread_mutex_unlock(&mutex);
}

void* fun(void* arg)
{
	pthread_mutex_lock(&mutex);
	printf("fun lock!\n");
	sleep(5);
	pthread_mutex_unlock(&mutex);
	printf("fun unlock!\n");	
}

int main()
{
	pthread_t id;
	pthread_mutex_init(&mutex,NULL);
	pthread_create(&id,NULL,fun,NULL);
	pthread_atfork(prepare,after,after);//放在锁的初始化后面,fork之前即可

	sleep(1);//保证函数线程一定结束
	pid_t pid=fork();
	if(pid==-1)
	{
		exit(1);
	}
	if(pid==0)
	{
		printf("child lock start!\n");
		pthread_mutex_lock(&mutex);
		printf("child lock success!\n");
		pthread_mutex_unlock(&mutex);
		exit(0);
	}
	wait(NULL);
	pthread_join(id,NULL);
	printf("main over!\n");
	exit(0);
}

  

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CRichEditUI中文乱码问题(Duilib)

这是遇到问题的时候&#xff0c;我还以为是韩文 解决方案&#xff1a; //HMODULE hmod LoadLibrary(_T("msftedit.dll"));HMODULE hmod LoadLibrary(_T("riched20.dll"));//修改一下使用的动态库&#xff0c;兼容性问题需要自己测

每日OJ题_链表②_力扣24. 两两交换链表中的节点

目录 力扣24. 两两交换链表中的节点 解析代码 力扣24. 两两交换链表中的节点 24. 两两交换链表中的节点 难度 中等 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&…

JavaWeb04-Request,Response

目录 一、Request&#xff08;请求&#xff09; 1.作用 2.继承体系 3.获取请求数据 &#xff08;1&#xff09;请求行 &#xff08;2&#xff09;请求头 &#xff08;3&#xff09;请求体&#xff08;POST&#xff09; &#xff08;5&#xff09;Request通用方式获取请求…

植物神经紊乱的五大信号,你知道吗?

植物神经紊乱&#xff0c;听起来像是医学名词&#xff0c;但其实它离我们的生活并不遥远。它就像一位隐形的朋友&#xff0c;时常悄悄地出现&#xff0c;给我们带来从头到脚的不适&#xff0c;让我们的生活变得困扰不已。今天&#xff0c;就让我们一起揭开这位“朋友”的真面目…

[Unity实战]使用NavMeshAgent做玩家移动

其实除了Character Controller, Rigidbody&#xff0c;我们还可以使用NavMeshAgent去做。这么做的好处是能避免玩家去莫名其妙的地方&#xff08;毕竟基于烘焙过的导航网格&#xff09;&#xff0c;一般常见于元宇宙应用和mmo。 根据Unity手册&#xff0c;NavMeshAgent 也有和…

【JavaEE初阶 -- 计算机核心工作机制】

这里写目录标题 1.冯诺依曼体系2.CPU是怎么构成的3.指令表4.CPU执行代码的方式5.CPU小结&#xff1a;6.编程语言和操作系统7. 进程/任务&#xff08;Process/Task&#xff09;8.进程在系统中是如何管理的9. CPU分配 -- 进程调度10.内存分配 -- 内存管理11.进程间通信 1.冯诺依曼…

QPaint绘制自定义仪表盘组件04

网上视频抄的&#xff0c;用来自己看一下&#xff0c;看完就删掉 最终效果 ui widgetspeed.h #ifndef WIDGETSPEED_H #define WIDGETSPEED_H#include <QWidget> #include <QPaintEvent> #include <QPainter> #include <QDebug> #include <QFont&g…

时光机关:探秘Java中的Timer和TimerTask

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 时光机关&#xff1a;探秘Java中的Timer和TimerTask 前言Timer和TimerTask的基本概念Timer&#xff1a;TimerTask&#xff1a;为何它们是 Java 中任务调度的得力工具&#xff1a; Timer的使用方法创建…

【物联网应用案例】从0到N,智慧农业的数据价值

智慧农业全方位渗透到农业的每一个环节&#xff0c;云端解决方案更推动了研究人员、农艺师及农民间的密切协作&#xff0c;为研发企业提供了既经济又具扩展性的完美方案。 据IDC预计&#xff0c;到2036年&#xff0c;农场收集的数据量将增加800%以上&#xff0c;这凸显了农业数…

一款非常适合老中医用的《书剑中医电子处方软件简明版》

上了年纪的老中医&#xff0c;虽然经验丰富&#xff0c;但是电脑的基础都比较差&#xff0c;而开处方的软件通常又设计的太复杂&#xff0c;想用电脑开处方就非常困难&#xff0c;所以只好坚持手写开处方。最近&#xff0c;小编找到了一款非常简单的《书剑中医电子处方软件简明…

GPQA数据集分享

来源: AINLPer公众号&#xff08;每日干货分享&#xff01;&#xff01;&#xff09; 编辑: ShuYini 校稿: ShuYini 时间: 2024-2-28 尽管AI系统在许多任务上表现出色&#xff0c;但在需要大量专业知识和推理能力的任务上仍然存在局限性。为此&#xff0c;纽约大学的研究者提出…

ECMAScript 语法

ECMAScript 语法 一、ECMAScript1.ECMAScript简介2.ECMAScript历史 二、ECMAScript 语法区分大小写变量是弱类型的每行结尾的分号可有可无注释与 Java、C 和 PHP 语言的注释相同括号表示代码块 一、ECMAScript ECMAScript是一种由Ecma国际&#xff08;前身为欧洲计算机制造商协…

西门子Mendix低代码资深技术顾问张戟,将出席“ISIG-低代码/零代码技术与应用发展峰会”

3月16日&#xff0c;第四届「ISIG中国产业智能大会」将在上海中庚聚龙酒店拉开序幕。本届大会由苏州市金融科技协会指导&#xff0c;企智未来科技&#xff08;LowCode低码时代、RPA中国、AIGC开放社区&#xff09;主办。大会旨在聚合每一位产业成员的力量&#xff0c;深入探索低…

【操作系统概念】 第7章:死锁

文章目录 0.前言7.1 系统模型7.2 死锁特征7.2.1 必要条件7.2.2 资源分配图 7.3 死锁处理方法7.4 死锁预防&#xff08;deadlock prevention&#xff09;7.4.1 互斥7.4.2 占有并等待7.4.3 非抢占7.4.4 循环等待 7.5 死锁避免&#xff08;deadlock-avoidance&#xff09;7.5.1 安…

NetSuite Mass Update 批量更新功能

NetSuite中有一个小而精的便捷功能&#xff0c;但是也是一个很容易在实践中被大家遗忘的隐藏功能&#xff0c;就是Mass Update批量更新&#xff0c;在此想和各位分享一下&#xff5e;该功能主要是可以帮助用户快速将符合固定标准的记录中的单个/多个字段直接进行批量更新。如果…

深度神经网络 基本知识 记录

资料&#xff1a;https://www.bilibili.com/video/BV1K94y1Z7wn/?spm_id_from333.337.search-card.all.click&vd_source14a476de9132ba6b2c3cbc2221750b99 计划&#xff1a;3~4天 注&#xff1a;网课讲的内容比较糅杂&#xff0c;记录的内容可能会出现重复 杂 人工智能…

vue3基础教程(3)——引入ui框架iview(viewui)

博主个人微信小程序已经上线&#xff1a;【中二少年工具箱】。欢迎搜索试用 正文开始 专栏简介1. 下载iview2.更新资源3.引入插件4.运行项目 专栏简介 本系列文章由浅入深&#xff0c;从基础知识到实战开发&#xff0c;非常适合入门同学。 零基础读者也能成功由本系列文章入门…

HTML开发工具和环境介绍,内附超详细的VS code安装教程!

工欲善其事必先利其器&#xff0c;一款好的开发工具可以让我们事半功倍。前面我们对HTML的相关概念和基本结构已经有了基本的了解&#xff0c;下面我们就来安装在前端开发中的需要使用的开发工具及环境。 在众多HTML编辑器中&#xff0c;选择一个适合自己的工具至关重要。今天…

实现的一个网页版的简易表白墙

实现的一个网页版的表白墙 实现效果 代码截图 相关代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><tit…

计算机网络-网络应用服务器(二)

目录 1.虚拟机VM简介&#xff1a; 2.虚拟机VM几个注意事项&#xff1a; 3.Web服务器网站配置&#xff1a; 4.FTP服务器选项设置&#xff1a; 5.隔离的FTP服务器安装设置&#xff1a; 6.Apache服务器&#xff1a; 7.httpd.conf主配置文件部分内容&#xff1a; 8.虚拟主机&a…