多线程进程fork出来的进程是单线程还是多线程?

一个多线程进程fork出来的进程是多线程还是单线程的?先说结论:是单线程的。

实践

口说无凭,我们先写段代码实践验证一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 来源:公众号【编程珠玑】
// 作者:守望先生
// multiThread.cc
#include <unistd.h>
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
std::atomic<bool> start{false};
void threadfunc() {
while (!start) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
while (start) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "thread func,pid:" << getpid() << std::endl;
}
}
int main() {
std::thread t1(threadfunc);
// daemon(0, 1); // 后台执行
start.store(true);
t1.join(); // 等待threadfunc运行结束
return 0;
}

编译运行:

1
2
3
4
5
$ g++ -o multiThread multiThread.cc -lphtread
$ ./multiThread
thread func,pid:9901
thread func,pid:9901
thread func,pid:9901

结果正常,线程不断循环打印信息。那如果启动线程后,再fork呢?即将代码中daemon的相关行的注释去掉,再编译运行。

在《如何让程序真正地后台运行?》中我们知道,daemon实际上做了进程的fork。

运行这个例子,我们会发现,程序立马退出了,没有打印我们预想的内容。

为什么

为什么会这样呢?实际上,我们在《如何使用fork创建进程》中就提到过,fork的时候会拷贝父进程的数据内容,即写时复制,但是,像启动运行的线程,是不会被“复制”过去的。也就是说,从父进程fork出来的子进程,将会是单线程的。这也就给了我们一些启示

  • 如果在API中需要启动工作线程,则工作线程需要在daemon化之后再启动

怎么理解呢?比如说,你设计了某一个功能,你的功能是需要启动一个线程来进程工作,那么你在使用的时候,就必须要特别注意这种fork进程的场景,即需要在fork之后启动线程,才能保证线程能够正常启动并工作。

守望 wechat
关注公众号[编程珠玑]获取更多原创技术文章
出入相友,守望相助!