线程安全——使用线程安全函数,多线程中执行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);
}

  

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

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

相关文章

《Effective Modern C++》- 极精简版 1-4条

本文章属于专栏《业界Cpp进阶建议整理》 本文列出《Effective Modern C》的1-5条的个人理解的极精简版本。 Item1、模版类型推导 个人理解&#xff1a;推导规则是繁琐的&#xff0c;和运算符优先级一样&#xff0c;背下来没有意义&#xff0c;最好的办法是在使用中熟悉。常用的…

vue3: vscode自动补充ref变量的.value属性(解决方式)

操作一下步骤后&#xff0c; 然后重启vscode即可 效果图&#xff1a;

Doris入门

概念&#xff1a; 基于MPP设计理念实现的数据库&#xff0c;支持大数据集存储、实时数据分析&#xff0c;交互式数据分析等。 MPP&#xff1a;将任务并行分散至多个节点&#xff0c;由各节点单独计算&#xff0c;再将各节点计算结果汇总得到最终结果。 原理&#xff1a; FE&…

thinkphp5框架漏洞

条件&#xff1a; 根据ThinkPHP版本&#xff0c;如是5.x版本&#xff0c;即可使用ThinkPHP 5.x远程代码执行漏洞&#xff0c;无需登录&#xff0c;即可执行任意命令&#xff0c;获取服务器最高权限。 具体版本 5.x < 5.1.31 5.0.x< 5.0.23 框架介绍&#xff1a; Thi…

CC攻击的特征和防护措施

随着互联网的快速发展&#xff0c;网络攻击日益频繁。在目前的各种网络攻击中&#xff0c;CC攻击是一种常见的网络攻击手段之一。CC攻击&#xff08;也称为DDoS攻击&#xff09;是指通过大量请求或恶意流量向目标网站或服务器发送请求&#xff0c;以使其服务不可用。可能大家都…

Mysql date_format 格式化时间输出

网上的文章都是列一个大而全的表格&#xff0c;还有错误&#xff0c;我完全用不到那么多形式。遂自己开篇文章记录。 把datetime格式化为形如 2024-12-15 18:59:59的形式&#xff1a; %Y-%m-%d %H:%i:%S# 日期格式化输出select date_format(now(),%Y-%m-%d %H:%i:%S) time1,no…

如何通过3D线上科技展厅全方位展现企业创新实力?

随着数字化转型的加速&#xff0c;企业对于科技展示的需求日益增长&#xff0c;而3D线上科技展厅作为一种新兴的展示方式&#xff0c;正逐步成为企业展示科技成果、产品特性的重要工具。 一、全方位展示展品 线上科技展厅能够实现产品的全方位、多角度展示&#xff0c;这不仅增…

SAP在S4中,资产模块年结

在S4中&#xff0c;资产模块也发生了一些变化&#xff1a; 财政年度变更事务代码AJRW已经过时。它已被交易FAGLGVTR&#xff08;“总账&#xff1a;结转余额”&#xff09;取代。 资产会计中的所有APC变化都实时发布到总账中。因此&#xff0c;周期性的APC过账是没有用的&…

unity代码热修改HotReload

HotReload for unity支持的代码修改 下载地址 HotReload的版本:1.12.9 Unity版本:2018,2019,2020,2021,2023 打开热重装有两种方法 ●方法1:通过导航到Unity菜单栏中的“窗口”打开热重装窗口&#xff0c;然后选择“热重装”。 ●方法2:使用快捷键组合“AltShiftH”快速打开…

Ubuntu环境配置-LinuxQQ篇

本教程下载Linux QQ的版本是linuxqq_3.0.0-571_amd64.deb 一、下载LinuxQQ 直接使用wget命令下载链接&#xff0c;下载文件 wget https://dldir1.qq.com/qqfile/qq/QQNT/c005c911/linuxqq_3.0.0-571_amd64.deb 二、安装LinuxQQ 当下载完成后&#xff0c;运行命令&#xff1a;…

抖音网红老隋分享的蓝海项目是否值得去做呢?

在互联网时代&#xff0c;各种创业项目层出不穷&#xff0c;其中不少被誉为“蓝海项目”&#xff0c;意味着市场潜力巨大&#xff0c;竞争相对较小。近期&#xff0c;抖音网红老隋分享了一个备受关注的蓝海项目&#xff0c;那么&#xff0c;这个项目究竟是否值得去做呢?本文将…

录制用户操作实现自动化任务

先上视频&#xff01;&#xff01; 流程自动化工具-录制操作绘制流程 这个想法之前就有了&#xff0c;趁着周末时间给它撸出来。 实现思路 从之前的文章自动化桌面未来展望中已经验证了录制绘制流程图的可行性。基于DOM录制页面操作轨迹的思路监听页面点击、输入事件即可&…