Linux Day16 多线程的一些常见问题

news2024/12/23 3:18:00

目录

一、多线程+fork()

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

1.1.1 不使用fork前,让线程函数和主程序打印其进程号

结果:

结论:

1.1.2 在主程序中加入fork

结果:

结论:

1.1.3 线程函数加入fork()

 结果:

结论:

综上所述:多线程程序fork后,子进程只启用一条执行路径,就是fork所在的执行路径。

 问题二: 父进程被加锁的互斥锁 fork 后在子进程中是否已经加锁

代码

结果

分析

解决

结果


一、多线程+fork()

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

1.1.1 不使用fork前,让线程函数和主程序打印其进程号

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *fun(void *arg)
{
    for (int i = 0; i < 5; i++)
    {
        printf("fun pid=%d\n", getpid());
        sleep(1);
    }
}
int main()
{
    pthread_t id;
    pthread_create(&id, NULL, fun, NULL);
    for (int i = 0; i < 5; i++)
    {
        printf("main pid=%d\n", getpid());
        sleep(1);
    }
    pthread_join(id,NULL);
    exit(0);
}

结果:

结论:

不难发现,线程函数和主函数的进程号是一样的

1.1.2 在主程序中加入fork

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *fun(void *arg)
{
    for (int i = 0; i < 5; i++)
    {
        printf("fun 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 pid=%d\n", getpid());
        sleep(1);
    }
    pthread_join(id,NULL);
    exit(0);
}

结果:

结论:

不难发现父进程中打印主线程和线程函数id=3519,而子进程执行了主线程id=3521,子进程只有一条执行路径。

1.1.3 线程函数加入fork()

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *fun(void *arg)
{
    fork();
    for (int i = 0; i < 5; i++)
    {
        printf("fun pid=%d\n", getpid());
        sleep(1);
    }
}
int main()
{
    pthread_t id;
    pthread_create(&id, NULL, fun, NULL);
    
    for (int i = 0; i < 5; i++)
    {
        printf("main pid=%d\n", getpid());
        sleep(1);
    }
    pthread_join(id,NULL);
    exit(0);
}

 结果:

结论:

不难发现父进程中打印主线程和线程函数id=3551,而子进程执行了线程函数id=3553,子进程只有一条执行路径。

综上所述:多线程程序fork后,子进程只启用一条执行路径,就是fork所在的执行路径。

 问题二: 父进程被加锁的互斥锁 fork 后在子进程中是否已经加锁

多线程中fork以后产生子进程,子进程共享父进程的内容,但是会不会共享锁或者信号量呢,下面我们举个栗子。

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include<sys/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_mutex_init(&mutex,NULL);
    pthread_t id;
    pthread_create(&id,NULL,fun,NULL);
    sleep(1);
    pid_t pid =fork();
    if(pid==-1)
    {
        exit(0);
    }
    if(pid==0)
    {
        printf("child 准备 lock\n");
        pthread_mutex_lock(&mutex);
        printf("child枷锁成功\n");
        pthread_mutex_unlock(&mutex);
        exit(0);
    }
    wait(NULL);
    printf("main over\n");
    exit(0);
}

结果

分析

代码从主程序开始执行,执行到线程函数时,创建线程,进入fun()后,加锁,打印“fun lock”,随后睡眠5秒,我们知道多线程是有并发这个特性,这个时候就会继续主函数,进行fork,这个时候我们发现打印了"child 准备lock",注意此时我们线程函数中的锁还没有解,就有了一个新的锁,说明父进程和子进程的锁不是共用一个锁,此后5秒睡眠时间结束,这时继续执行多线程函数,解锁打印“fun unlock”,但是我们发现一件事:此函数阻塞了。

接下来就是这个问题的核心之所在。

fork()会将父进程的内容给子进程复制一份,同时也会把锁的状态给子进程,如在fork前锁还没有上,那么复制给子进程的锁就是没有上的。所以这里我们在fork前父进程就已经上了锁,传递给子进程后,子进程刚开始的锁就是上锁状态,所以就不会执行上锁状态,因为没有解锁。

解决

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

该函数通过3个不同阶段的回调函数来处理互斥锁状态。参数如下:
prepare:将在fork调用创建出子进程之前被执行,它可以给父进程中的互斥锁对象明明确确上锁。这个函数是在父进程的上下文中执行的,正常使用时,我们应该在此回调函数调用 pthread_mutex_lock 来给互斥锁明明确确加锁,这个时候如果父进程中的某个线程已经调用pthread_mutex_lock给互斥锁加上了锁,则在此回调中调用 pthread_mutex_lock 将迫使父进程中调用fork的线程处于阻塞状态,直到prepare能给互斥锁对象加锁为止。

parent: 是在fork调用创建出子进程之后,而fork返回之前执行,在父进程上下文中被执行。它的作用是释放所有在prepare函数中被明明确确锁住的互斥锁。
child: 是在fork返回之前,在子进程上下文中被执行。和parent处理函数一样,child函数也是用于释放所有在prepare函数中被明明确确锁住的互斥锁。

函数成功返回0, 错误返回错误码。
 

pthread_mutex_t mutex;

void fork_lock(void)
{
    pthread_mutex_lock(&mutex);
}

void fork_unlock(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_mutex_init(&mutex,NULL);
    pthread_t id;
    pthread_atfork(fork_lock,fork_unlock,fork_unlock);
    pthread_create(&id,NULL,fun,NULL);
    sleep(1);

    pid_t pid = fork();
    if ( pid == -1 )
    {
        exit(1);
    }

    if ( pid == 0 )
    {
        printf("child 准备lock\n");
        pthread_mutex_lock(&mutex);//阻塞
        printf("child加锁成功\n");
        pthread_mutex_unlock(&mutex);

        exit(0);
    }

    wait(NULL);
    printf("main exit\n");
    exit(0);

}

结果

到这里线程的同步就更新这么多啦,明天更新生产者消费者模型。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

个人博客网站一揽子:Docker搭建图床(Lsky Pro)

Lsky Pro 介绍 Lsky Pro 是一个用于在线上传、管理图片的图床程序&#xff0c;中文名&#xff1a;兰空图床&#xff0c;你可以将它作为自己的云上相册&#xff0c;亦可以当作你的写作贴图库。 兰空图床始于 2017 年 10 月&#xff0c;最早的版本由 ThinkPHP 5 开发&#xff0…

​bing许少辉乡村振兴战略下传统村落文化旅游设计images

​bing许少辉乡村振兴战略下传统村落文化旅游设计images

JavaEE学习之--类和对象

&#x1f495;粗缯大布裹生涯&#xff0c;腹有诗书气自华&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;Java学习之--类和对象 类和对象 类的实例化&#xff1a; 1.什么叫做类的实例化 利用类创建一个具体的对象就叫做类的实例化&#xff01; 当我们创建了…

【消息中间件】详解mq消息积压

作者简介 目录 1.产生原因 2.解决办法 2.1.事前处理机制 2.2.事中处理机制 2.3.事后处理机制 1.产生原因 消息积压&#xff08;Message Backlog&#xff09;指的是在消息队列&#xff08;MQ&#xff09;系统中等待被处理的消息数量超过了正常的处理速度&#xff0c;导致消…

Nvm任意切换node版本号

前言&#xff1a; nvm&#xff08;Node Version Manager&#xff09;是一个用于管理Node.js版本的工具。它允许您在同一台计算机上同时安装和切换不同版本的Node.js。使用nvm&#xff0c;您可以轻松地在项目之间切换Node.js版本&#xff0c;而无需手动安装和卸载不同的版本。这…

FPGA纯verilog实现8路视频拼接显示,提供工程源码和技术支持

目录 1、前言版本更新说明免责声明 2、我已有的FPGA视频拼接叠加融合方案3、设计思路框架视频源选择OV5640摄像头配置及采集静态彩条视频拼接算法图像缓存视频输出 4、vivado工程详解5、工程移植说明vivado版本不一致处理FPGA型号不一致处理其他注意事项 6、上板调试验证并演示…

【Java 基础篇】Java 标准输出流详解:输出你的程序之美

Java 编程中&#xff0c;标准输出流是一个重要的概念。它允许我们将程序的输出信息显示在终端或控制台上&#xff0c;这对于调试、用户界面和与用户的交互非常重要。在这篇文章中&#xff0c;我们将深入探讨 Java 的标准输出流&#xff0c;了解如何使用它以及一些常见的用法和技…

libevent数据结构——TAILQ_结构体

TAILQ_结构体 TAILQ_结构体在文件event2/event_struct.h和文件event2/keyvalq_struct.h中都有定义&#xff0c;并且他们的定义都是一样的&#xff0c;定义了TAILQ_ENTRY、TAILQ_HEAD结构体&#xff1a; #ifndef TAILQ_ENTRY #define EVENT_DEFINED_TQENTRY_ #define TAILQ_EN…

JVM——10.对象的内存布局

这篇文章&#xff0c;我们来了解一下对象在内存中的布局是什么样的。 解释&#xff1a;前面有一篇文章我们讲了JVM中类的结构&#xff0c;那里讲的是一个java类&#xff0c;被编译成二进制字节码后&#xff0c;它的结构是什么样的&#xff0c;或者说按照jvm的标准&#xff0c;…

故障注入实验:了解如何使用Chaos Engineering的方法,在服务网格中进行故障注入实验

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

Jenkins自动化部署前后端分离项目 (svn + Springboot + Vue + maven)有图详解

1. 准备工作 本文的前后端分离项目&#xff0c;技术框架是&#xff1a; Springboot Vue Maven SVN Redis Mysql Nginx JDK 所以首先需要安装以下&#xff1a; 在腾讯云服务器OpenCLoudOS系统中安装jdk&#xff08;有图详解&#xff09; 在腾讯云服务器OpenCLoudOS系统…

ElasticSearch+MongoDB:搜索-关键字联想

目标&#xff1a; 从长尾关键词挖掘站长工具智能改写 - 5118营销大数据中获得数据集 业务层 java Autowired MongoTemplate mongoTemplate; /*** 联想词* param userSearchDto* return*/ Override public ResponseResult findAssociate(UserSearchDto userSearchDto) {//1 参数…

JUC并发工具使用与详情(CountDownLatch、CyclicBarrier、Semaphore、Exchanger)

JUC并发工具 一、CountDownLatch应用&源码分析 1.1 CountDownLatch介绍 CountDownLatch就是JUC包下的一个工具&#xff0c;整个工具最核心的功能就是计数器 如果有三个业务需要并行处理&#xff0c;并且需要知道三个业务全部都处理完毕了 需要一个并发安全的计数器来操…

大型语言模型:SBERT — 句子BERT

了解 siamese BERT 网络如何准确地将句子转换为嵌入 简介 Transformer 在 NLP 领域取得了进化性的进步&#xff0c;这已不是什么秘密。基于 Transformer&#xff0c;还发展出了许多其他机器学习模型。其中之一是 BERT&#xff0c;它主要由几个堆叠的 Transformer 编码器组成。除…

RabbitMQ生产故障问题分析

1. 问题引发 由某个服务BI-collector-xx队列出现阻塞&#xff0c;影响很整个rabbitMQ集群服务不可用&#xff0c;多个应用MQ生产者服务出现假死状态&#xff0c;系统影响面较广&#xff0c;业务影响很大。当时为了应急处理&#xff0c;恢复系统可用&#xff0c;运维相对粗暴的把…

使用Linkerd实现流量管理:学习如何使用Linkerd的路由规则来实现流量的动态控制

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

笔试面试相关记录(5)

&#xff08;1&#xff09;给定一个字符串&#xff0c;含有大写、小写字母&#xff0c;空格&#xff0c;数字&#xff0c;需要将其变为满足如下条件&#xff1a; 所有的数字需要换成空格&#xff0c;并且字符串的头尾不包含空格&#xff0c;且整个字符串不包含连续的两个空格。…

网络地址转换技术NAT以及路由器LAN口与WAN口的数据交换

NAT技术 网络地址转换&#xff08;NAT&#xff09;技术可以帮助局域网设备通过私有IP地址访问互联网。以下是NAT技术如何实现这一功能的基本原理&#xff1a; 私有IP地址&#xff1a;在一个局域网中&#xff0c;通常使用私有IP地址来为设备分配网络标识。私有IP地址范围包括以…

OT:数字设定框(QSpinBox:处理整数,QDoubleSpinBox:处理浮点数)

widget.h #ifndef WIDGET_H #define WIDGET_H //数字设定框 #include <QWidget> #include <QSpinBox> //处理整数 #include <QDoubleSpinBox> //处理浮点数class Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent 0);~Widget();private:…

RabbitMQ 消息应答

每日一句 物是人非事事休,欲语泪先流。 概述 为了保证消息在发送过程中不丢失,RabbitMQ引入了消息应答机制, 消费者在接收到消息并且处理该消息后,告诉RabbitMQ它已经处理了,RabbitMQ可以把消息删除了。 自动应答 消息发送后立即被认为已经传送成功,这种模式需要在…