进程:
编写完毕的代码,在没有运行的时候,称之为程序;
正在运行的代码,就称之为进程;
进程,除了包含代码以外,还有需要运行的环境等,所以和程序是有区别的;
fork() 方法创建子进程:
Python 的 os 模块,封装了常见的系统调用,其中就包括 fork(),可以在 python 程序中轻松的创建子进程;
注意:fork() 方法只能用在类 linux 系统下,不能用在 windows 系统下;
先看下面一段代码:
# 导入 os 模块
import os
# 调用 fork() 方法创建子进程
os.fork()
print("hello")
输出结果:
可以发现,代码里只有一个输出语句,但是实际上输出了两次;
这是因为:
-
程序执行到 os.fork() 时,操作系统会创建一个新的进程(子进程),然后复制父进程的所有信息到子进程中;
-
然后父进程和子进程都会从 fork() 函数中得到一个返回值,子进程永远返回 0,而父进程返回子进程的 id 号;
将上面代码改成如下所示:
# 导入 os 模块
import os
# 调用 fork() 方法创建子进程,并获取返回值
pid = os.fork()
# 输出 fork() 方法返回的值
print("pid:", pid)
输出结果为:
如果子进程中有耗时的操作,当父进程先执行结束的时候,虽然程序看似已经结束了,但是子进程并不会立刻结束,而是等子进程中的代码执行完毕之后才会结束:
import os
import time
# 调用 fork() 方法创建子进程,并获取返回值
pid = os.fork()
# 输出 fork() 方法返回的值
print("pid:", pid)
# 在子进程中模拟耗时操作
if (pid == 0):
print("=== 子进程开始 ===")
time.sleep(2) # 延迟 2 秒
print("=== 子进程结束 ===")
else:
print("=== 父进程 ===")
输出结果:
os.getpid() 和 os.getppid():
getpid():获取当前进程的 id(get process id);
getppid():获取父进程的 id(get parent process id);
# 导入 os 模块
import os
# 调用 fork() 方法创建子进程,并获取返回值
pid = os.fork()
# 输出 fork() 方法返回的值
print("pid:", pid)
# os.getpid():获取当前进程 id
# os.getppid():获取父进程 id
if (pid > 0):
print("父进程:", os.getpid())
else:
print("子进程:%d === 父进程:%d" %(os.getpid(), os.getppid()))
输出结果:
多进程修改全局变量:
多进程中,每个进程中所有数据(包括全局变量)都各自拥有一份,互不影响;即多进程数据不共享;
import os
import time
# 定义一个全局变量
num = 10
# 创建子进程
pid = os.fork()
if (pid == 0):
# 在子进程中改变 num 的值
num += 10
print("子进程:", num)
else:
# 延迟 1 秒,是为了保证子进程一定先执行完毕
time.sleep(1)
print("父进程:", num)
输出结果:
多个 fork() 的情况:
先看下面一段代码:
import os
# 第一次使用 fork 创建子进程
pid = os.fork()
if pid == 0:
print("=== 1 ===")
else:
print("=== 2 ===")
# 第二次使用 fork 创建子进程
pid = os.fork()
if pid == 0:
print("=== 11 ===")
else:
print("=== 22 ===")
输出结果:
原理分析:
-
当程序运行到 “第一次使用 fork 创建子进程时”,此时有两个进程,一个父进程,一个子进程;
-
我们知道,程序在创建子进程的时候,会把父进程中的所有信息全部复制到子进程中;那么就是说 “第二次使用 fork 创建子进程” 的代码,在父进程和子进程中各有一份;
-
所以,当父进程执行到 “第二次使用 fork 创建子进程” 的时候,又创建了一个子进程,就是说一个父进程创建了两个子进程;此时会输出一遍 11 和 22;
-
而当 “第一次使用 fork 创建的子进程” 也执行到 “第二次使用 fork 创建子进程” 时,子进程又创建了一个孙子进程,那么理所当然的子进程就变成了孙子进程的父进程;那么此时又会再输出一遍 11 和 22;
-
所以 11 和 22 总共输出了两次;
-
程序中总共有 4 个进程,分别是父进程,大儿子进程,二儿子进程,以及大儿子进程的子进程;