首页 > 系统相关 >【Linux】多线程中fork与互斥锁---子进程会复制继承父进程里锁的状态

【Linux】多线程中fork与互斥锁---子进程会复制继承父进程里锁的状态

时间:2022-09-22 14:00:32浏览次数:74  
标签:fun include printf 互斥 unlock mutex pthread 进程 多线程

摘自:https://blog.csdn.net/xiaoxiaoguailou/article/details/121617142

问题提出:
我们有这样一个问题:在一个多线程程序中创建子进程并且让子线程和子进程去获取一把全局变量的锁,输出子线程得到锁,然后解锁,子进程拿到锁,然后解锁;

(一)初次尝试
代码:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/wait.h>

pthread_mutex_t mutex;

void* fun(void* arg)
{
pthread_mutex_lock(&mutex);
printf("fun get lock\n");
sleep(3);

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)
{
perror("fork err");
return -1;
}

if(pid == 0)
{
pthread_mutex_lock(&mutex);
printf("child get lock\n");
sleep(3);

pthread_mutex_unlock(&mutex);
printf("child unlock\n");
exit(0);
}

wait(NULL);

pthread_mutex_destroy(&mutex);
printf("main over\n");
return 0;
}

 

猜想结果:
理一理思路:

创建一把全局的锁,并初始化
主线程创建子线程,去获取这把锁的同时,主线程休眠一秒,子线程得到这把锁,加锁fun get lock
子线程休眠3秒,输出解锁fun unlock,子线程退出
主线程开始fork,子进程得到这把锁,输出child lock
子进程解锁输出child unlock
父进程的主线程等待子进程的退出,最后销毁锁,输出main over
所以…直接得到正确代码!!!
(二)理性分析
很遗憾,答案是错的!!!
我们先来康康运行结果:正常输出了子线程的内容,但是。。。。卡住了?没错阻塞了。。

 

 再次分析:

阻塞??难道是子进程获取锁时阻塞了?还是父进程等待子进程阻塞了?
或者说:两处地方都阻塞了,子进程获取锁时阻塞,导致父进程阻塞。
验证一下!!

 

 

所以程序就是两处地方都阻塞了,子进程获取锁时阻塞,导致父进程阻塞。

(三)解决问题
实际上,我们子线程在获取这把锁并加锁后睡眠3秒,主线程睡眠1秒,在主线程1秒睡眠结束后,开始执行fork,此时的子线程还未解锁正处于睡眠状态,fork过程中,会直接复制父进程的所有资源(**包括这把锁、锁的状态**),没错就是有两把锁。此时子线程进行了加锁的状态,所以子线程复制的锁也是加锁状态。所以子线程会正常退出,而子进程会因为拿到加锁的锁而阻塞,父进程wait因为子进程阻塞而阻塞;

(1)使用pthread_join()
fork之前使用pthread_join()在子线程结束之前,主线程都将会阻塞,等待子线程结束后,在进行fork,此时子进程得到的锁就是解锁状态

代码:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/wait.h>

pthread_mutex_t mutex;

void* fun(void* arg)
{
pthread_mutex_lock(&mutex);
printf("fun get lock\n");
sleep(3);

pthread_mutex_unlock(&mutex);
printf("fun unlock\n");
}
int main()
{
pthread_mutex_init(&mutex, NULL);
pthread_t id;
pthread_create(&id, NULL, fun, NULL);
pthread_join(id, NULL);

sleep(1);

pid_t pid = fork();
if(pid == -1)
{
perror("fork err");
return -1;
}

if(pid == 0)
{
pthread_mutex_lock(&mutex);
printf("child get lock\n");
sleep(3);

pthread_mutex_unlock(&mutex);
printf("child unlock\n");
exit(0);
}

wait(NULL);

pthread_mutex_destroy(&mutex);
printf("main over\n");
return 0;
}

 

结果:

 

 

(2)使用phread_atfork()注册一个fork之前的判断
头文件 : pthread.h
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));

prepare : fork执行之前调用该函数
parent : fork执行之后父进程调用该函数
child : fork执行之后子进程调用该函数
返回值: 成功0,失败错误码
代码:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/wait.h>

pthread_mutex_t mutex;

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

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

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

void* fun(void* arg)
{
pthread_mutex_lock(&mutex);
printf("fun get lock\n");
sleep(3);

pthread_mutex_unlock(&mutex);
printf("fun unlock\n");
}

int main()
{

pthread_mutex_init(&mutex, NULL);
pthread_t id;
pthread_atfork(prepare_fun, parent_fun, child_fun);
pthread_create(&id, NULL, fun, NULL);

sleep(1);

pid_t pid = fork();
if(pid == -1)
{
perror("fork err");
return -1;
}

if(pid == 0)
{
pthread_mutex_lock(&mutex);
printf("child get lock\n");
sleep(3);

pthread_mutex_unlock(&mutex);
printf("child unlock\n");
exit(0);
}

wait(NULL);

pthread_mutex_destroy(&mutex);
printf("main over\n");
return 0;
}

 


结果:

 

(3)我的方法更粗暴

在fork()后的子进程中立即解锁(无论是否已上锁)

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/wait.h>

pthread_mutex_t mutex;


void* fun(void* arg)
{
pthread_mutex_lock(&mutex);
printf("fun get lock\n");
sleep(3);

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)
{
perror("fork err");
return -1;
}

if(pid == 0)
{
pthread_mutex_unlock(&mutex); // 加此行防止进程复制过来时锁状态为"已上锁"

pthread_mutex_lock(&mutex); printf("child get lock\n"); sleep(3); pthread_mutex_unlock(&mutex); printf("child unlock\n"); exit(0); } wait(NULL); pthread_mutex_destroy(&mutex); printf("main over\n"); return 0; }

 

标签:fun,include,printf,互斥,unlock,mutex,pthread,进程,多线程
From: https://www.cnblogs.com/LiuYanYGZ/p/16719003.html

相关文章

  • OD调试子进程
    使用OD进行调试子进程,当一个进程创建了子进程后OD则无法跟踪(进程创建后,极短时间内执行完毕,关闭进程。没时间附加)。可以使用此方式。在创建进程前停下,找到子进程可执行文......
  • JAVA多线程-学习笔记
    1.1概述程序:程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。进程(Porcess):是执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位。线......
  • 多线程
    一.Java构建线程的方式继承Thread实现Runnable实现Callable线程池方式推荐手动创建线程池二.线程池的7个参数publicThreadPoolExecutor(intcorePoolSiz......
  • 使用pm2管理Node进程
    1.PM2是啥简单而言,就是一个自带负载均衡的node应用进程管理器2.为什么使用PM2在使用nodejs启动项目时,一个项目就会对应一个终端,而且如果终端关闭了,项目也就无......
  • 【Redis】Redis是单线程还是多线程
     Redis6.0版本之前的单线程指的是其网络I/O和键值对读写是由一个线程完成的Redis6.0引入的多线程指的是网络请求过程采用了多线程,而键值对读写命令仍然是单线程处......
  • 多线程04
    小结1.继承Thread类子类继承Thread类具备多线程能力启动线程:子类对象.start()不建议使用,因为继承是单继承2.实现Runnable接口......
  • dotnet 6 通过 DOTNET_ROOT 让调起的应用的进程拿到共享的运行时文件夹
    我的应用是独立发布的,在用户的设备上不需要额外去安装.NET运行时。但是我的应用有一个需求是下载另一个应用作为插件,由本应用调起插件进程。本文告诉大家如何解决调用插......
  • 多线程
    一、线程概念    进程是正在运行的程序,是系统资源调度的基本单位,一个进程至少有一个线程,线程中可以共享内存资源   例如:进程执行多件事情,例如一遍听音乐,一......
  • Hadoop集群启动没有DataNode进程
    问题状况:问题原因:在启动Hadoop之前,进行了多次格式化,导致DataNode的ID发生了变化解决方案:我们可以删除从节点所有的DataNode资料,并重新格式化解决流程1、根据core-si......
  • Java 多线程中的任务分解机制-ForkJoinPool,以及CompletableFuture
    简介ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后......