第10章 线程控制(5)_多线程下的fork
6. 线程和fork
6.1 多线程下的fork
(1)历史包袱
①fork与多线程的协作性很差,这是POSIX系统操作系统的历史包袱。
②长期以来程序都是单线程的,fork运行正常,但引入线程这后,fork的适用范围大大缩小。
(2)多线程下的fork
①在多线程执行的情况下调用fork函数,仅会将发起调用的线程复制到子进程中去(线程ID与父进程发起fork调用的线程ID一样)。也就是说,不能同时创建出与父进程一样多的子线程。
②其他线程均在子进程中立即停止并消失,并且不会为这些线程调用清理函数以及针对线程局部存储变量的析构函数,这可能会造成子进程的内存泄漏。
③虽然只有fork调用线程被复制到子进程,但子进程继承整个地址空间的副本,也从父进程那里继承了所有互斥量、读写锁和条件变量的状态。这意味着,如果某个线程锁定了某个互斥锁,那么在子进程中这个互斥锁可能因得不到释放而造成死锁现象的发生。
(3)多线程中fork的使用方法
①方法1:调用fork以后,立即调用exec()函数执行另一个程序,彻底隔断子进程与父进程的关系。由新的进程覆盖掉原有的内存,使用子进程中所有的线程相关的对象消失。
②方法2:使用pthread_atfork() + fork来创建子进程。
【编程实验】“死锁”现象的产生
//pthread_fork.c
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> //线程函数
void* th_func(void* arg)
{
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; printf("process(%d)_thread(%x) lock mutex\n", getpid(), pthread_self());
pthread_mutex_lock(&mutex); struct timespec ts = {, };
nanosleep(&ts, ); //休眠10秒 pthread_mutex_unlock(&mutex);
printf("process(%d)_thread(%x) unlock mutex\n", getpid(), pthread_self());
} int main(void)
{ pthread_t th;
int err = ; //创建子线程(注意,这个子线程只会存在于父进程中,子进程没有!)
if((err = pthread_create(&th, NULL, th_func, NULL)) !=){
perror("pthread_create error");
} sleep(); pid_t pid;
pid = fork(); //刚fork出来的子进程只能是单线程的,它只保留了调用fork
//的线程,而不保留父进程的其它线程。) if(pid <){
perror("fork error");
exit();
}else if(pid > ){ //parent process
printf("parent process id = %d thread id = %x\n", getpid(), pthread_self());
pthread_join(th, ); //等待子线程结束
}else{ //child process
printf("child process id = %d thread id = %x\n", getpid(), pthread_self());
th_func();
} wait(pid); //等待子进程结束 return ;
}
/*输出结果
process(1710)_thread(b77cab70) lock mutex //父进程中的子线程获得mutex,此时mutex为被占用状态
parent process id = 1711 thread id = b77cb6c0 //父进程调用fork后,mutex的状态会被子进程继承!
child process id = 1713 thread id = b77cb6c0 //创建子进程
process(1713)_thread(b77cb6c0) lock mutex //子进程的主线程试图获得mutex,因复制了父进程的mutex锁
//的状态。所以子进程的主线程(也是唯一的线程)会一直被阻塞!
process(1711)_thread(b77cab70) unlock mutex //父进程释放mutex
^C //子进程会因其主线程被阻塞而无法退出!
*/
6.2 pthread_atfork函数
(1)pthread_atfork()函数
|
头文件 |
#include <pthread.h> |
|
函数 |
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)); |
|
功能 |
安装fork处理函数 |
|
返回值 |
成功返回0,否则返回错误编号 |
|
备注 |
①prepare处理函数数在父进程调用fork之后,创建子进程前调用。 ②parent处理函数在创建子进程之后,但fork返回之前在父进程环境中执行。 ③child处理函数创建子进程之后,fork返回之前被调用。在子进程环境中调用。 ④可以多次调用pthread_atfork来安装多个fork处理函数。其中parent和child的处理程序是以它们注册的顺序调用的。而prepare处理程序的调用是与其注册时的顺序相反。 |
【编程实验】子进程中清理互斥锁
//pthread_atfork.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h> /*清理mutex锁
1、定义了两个互斥锁。
2、prepare处理函数中获取这两个锁
3、child处理函数在子进程环境中释放锁
4、parent处理函数在父进程环境中释放锁
*/
pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER; //在fork出子进程之前调用,因此只会在父进程中执行!
void prepare(void)
{
printf("process(%d) preparing locks...\n", getpid());
pthread_mutex_lock(&lock1);
pthread_mutex_lock(&lock2);
} void parent(void)
{
printf("parent unlocking locks...\n");
pthread_mutex_unlock(&lock1);
pthread_mutex_unlock(&lock2);
} void child(void)
{
printf("child unlocking locks...\n");
pthread_mutex_unlock(&lock1);
pthread_mutex_unlock(&lock2);
} void* th_func(void* arg)
{
printf("thread started...\n");
sleep();
return ;
} int main(void)
{
int err = ;
pid_t pid;
pthread_t tid; //可以多次调用pthread_atfork来安装多个fork处理函数.
//为简单起见,这里只调用一次。
if((err = pthread_atfork(prepare, parent, child)) != ){
perror("can't install fork handlers");
} //创建子线程
if((err = pthread_create(&tid, NULL, th_func, NULL)) != ){
perror("can't create thread");
} sleep();//让出CPU,让子线程去运行! printf("parent about to fork...\n"); //创建子进程
pid = fork(); //子进程只继承父进程中调用fork的线程(即主线程)
if(pid < ){
perror("fork error");
exit();
}else if(pid == ){ //child process
printf("child returned from fork\n");
}else{
printf("parent returned from fork\n");
} wait(pid); //等待子进程结束
return ;
}
/* 输出结果
thread started...
parent about to fork...
process(1786) preparing locks... //父进程获得两个锁
parent unlocking locks...
parent returned from fork
child unlocking locks... //子进程复制父进程的两个锁状态,这里成功释放锁
child returned from fork
*/
7. 线程和I/O
(1)进程中的所有线程共享相同的文件描述符。
(2)文件的定位(lseek)和读写(read/write)是两个操作,如果两个线程同时对同一个文件进行并发读写操作时,可能会造成不安全。
(3)而pread/pwrite将定位和读/写操作变成一个原子操作。这极大方便多线程下对文件的操作。如以下读取文件是线程安全的。
①pread(fd, buf1, 100, 300); //确保线程A读取偏移量为300处的记录
②pread(fd, buf2, 100, 700); //确保线程B读取偏移量为700处的记录
第10章 线程控制(5)_多线程下的fork的更多相关文章
- 《UNIX环境高级编程》(APUE) 笔记第十二章 - 线程控制
12 - 线程控制 GitHub 地址 1. 线程限制 下图为与 线程操作 有关的一些 限制: 可以通过 sysconf 函数进行查询 . 2. 线程属性 可使用 pthread_attr_t 结构修 ...
- 《APUE》读书笔记第十二章-线程控制
本章中,主要是介绍控制线程行为方面的内容,同时介绍了在同一进程中的多个线程之间如何保持数据的私有性以及基于进程的系统调用如何与线程进行交互. 一.线程属性 我们在创建线程的时候可以通过修改pthrea ...
- apue学习笔记(第十二章 线程控制)
本章将讲解控制线程行为方面的详细内容,而前面的章节中使用的都是它们的默认行为 线程属性 pthread接口允许我们通过设置每个对象关联的不同属性来细调线程和同步对象的行为.管理这些属性的函数都遵循相同 ...
- 第10章 系统级I/O(下)
10.7 I/O重定向 Unix外壳提供了I/O重定向操作符,允许用户将磁盘文件和标准输出输入联系起来. 例如:unix>ls>foo.txt,使得外壳加载和执行ls程序,将标准输出重定 ...
- 测试AtomicInteger与普通int值在多线程下的递增操作
日期: 2014年6月10日 作者: 铁锚 Java针对多线程下的数值安全计数器设计了一些类,这些类叫做原子类,其中一部分如下: java.util.concurrent.atomic.AtomicB ...
- 測试AtomicInteger与普通int值在多线程下的递增操作
日期: 2014年6月10日 作者: 铁锚 Java针对多线程下的数值安全计数器设计了一些类,这些类叫做原子类,当中一部分例如以下: java.util.concurrent.atomic.Atomi ...
- Linux——多线程下解决生产消费者模型
我们学习了操作系统,想必对生产消费者问题都不陌生.作为同步互斥问题的一个经典案例,生产消费者模型其实是解决实际问题的基础模型,解决很多的实际问题都会依赖于它.而此模型要解决最大的问题便是同步与互斥.而 ...
- C# 语言规范_版本5.0 (第10章 类)
1. 类 类是一种数据结构,它可以包含数据成员(常量和字段).函数成员(方法.属性.事件.索引器.运算符.实例构造函数.静态构造函数和析构函数)以及嵌套类型.类类型支持继承,继承是一种机制,它使派生类 ...
- Java多线程系列--“基础篇”10之 线程优先级和守护线程
概要 本章,会对守护线程和线程优先级进行介绍.涉及到的内容包括:1. 线程优先级的介绍2. 线程优先级的示例3. 守护线程的示例 转载请注明出处:http://www.cnblogs.com/skyw ...
随机推荐
- 一张图让你学会Python
有编程基础的人一看就可以了解 Python 的用法了.真正的 30 分钟上手.国外一高手画的,现把它翻译成中文,入门超简单python入门神图 *单击放大
- 安装Linux Mint17
韩总有台笔记本之前安装的是Win7,结果被她用成含毒,含马的机器了,最后干脆机器操作不了,愤怒的韩总把戴尔骂了个痛快并保证以后再也不用戴尔的笔记本了,然后愉快的换了一台新电脑,这台机器便放在我这里没人 ...
- Swift中获取系统语言
//en-US zh-HK zh-TW zh-Hans-US var lng:String { let userDefault = NSUserDefaults.standardUserDefault ...
- 推荐两个Magento做的中文网站 GAP和佰草集
Magento这两年发展很快,可以算是现阶段最有前途的开源电子商务系统,国外用的人很多,相对应的,国内也已经有很多人在用Magento建站了,可惜的是这其中绝大多数还是英文站,大多是国内外贸商建的外贸 ...
- OK335xS Ubuntu 12.04.1 版本 Android 开发环境搭建
/******************************************************************************************** * OK33 ...
- Codeforces123E. Maze【树形dp】【概率dp】【证明题】
LINK 题目大意 一棵树,上面的每个点都有一定概率成为起点和终点 从起点出发,随机游走,并按照下列规则统计count: DFS(x) if x == exit vertex then finish ...
- 新建Android一个项目-菜鸟篇
①打开Eclipse,单击菜单栏的“File”->把鼠标光标移动到“New”->在弹出的列表框中,如果直接能看到“Android Applicaion Project”选项项,则直接单击此 ...
- set 与 map 的第一次尝试
map 杭电6015http://acm.hdu.edu.cn/showproblem.php?pid=6015 基本用法:map<string,int>mp; mp[class[ i ...
- python之concurrent.futures模块
一.concurrent.futures模块简介 concurrent.futures 模块提供了并发执行调用的高级接口 并发可以使用threads执行,使用ThreadPoolExecutor 或 ...
- 【python下使用OpenCV实现计算机视觉读书笔记3】读写视频文件
代码例如以下: import cv2 videoCapture = cv2.VideoCapture('car.avi') fps = videoCapture.get(cv2.cv.CV_CAP_P ...