DC's blog DC's blog
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)

DC

愿我一生欢喜,不为世俗所及.
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)
  • python面向过程

  • python面向对象

  • 网络并发编程

    • 计算机网络储备
    • TCP简单实现
    • TCP粘包问题
    • 文件传输、UDP
    • socketserver模块
    • 进程理论储备
    • 进程开发必用
      • 开启子进程的两种方式
        • Process类实例化
        • 实现代码
        • 执行过程
        • 注意事项
        • 继承Process类
        • 实现代码
        • 与第一种方式的区别
      • 僵尸进程与孤儿进程
        • 抛出问题
        • 理论阐述
      • 进程内存空间彼此隔离
      • 进程对象的方法和属性
        • join方法!!
        • 实现代码
        • 举一反三
        • 代码优化
        • pid属性
        • 子进程是py进程
        • PID查看
        • 注意:join与pid
        • name属性
        • terminate,is_alive
    • 进程开发必知
    • 生产者消费者模型
    • 线程开发
    • GIL详解
    • 进程池与线程池
    • 协程
    • 网络IO模型
    • 轮询长轮询
    • channels
    • 小项目之手撸ORM
    • 小项目之仿优酷
    • 脚本编写
  • 不基础的py基础

  • 设计模式

  • python_Need
  • 网络并发编程
DC
2022-10-20
目录

进程开发必用

参考链接: https://www.cnblogs.com/linhaifeng/articles/7428874.html

# 开启子进程的两种方式

开启子进程的目的:
       想把父进程里串行执行的任务(eg,一段函数代码)分给独立的进程去执行,从而实现任务的并发执行!

# Process类实例化

我们通常使用的就是这一种!!

# 实现代码
# -- a.py
import time
from multiprocessing import Process


def task(x):
    print(f"{x} is running.")
    time.sleep(3)
    print(f"{x} is done.")


if __name__ == '__main__':
    # -- 参数说明:
    #    group不用管;target是我们的任务/子进程;name子进程的名字,不传会有个默认值;
    #    给我们的任务传参,有两种形式,args"元组,按位置传 注意元组里的逗号"、kwargs"字典"任选其一即可
    # Process(target=task, kwargs={'x': '子进程'})
    p = Process(target=task, args=('子进程',))  # -- 调用类实例化一个对象p
    # -- 只是在向操作系统发送一个开启p对象对应任务/子进程的信号,该行代码运行会非常非常的快.
    #    信号发完后,这个子进程什么时候造,造多长时间,运行多久,OS说的算,应用程序是管不了的!!
    p.start()
    # time.sleep(5)  # -- 可以实验下,运行该代码,"主"会最后打印.证明等待的5s的时间里子进程造出来啦!
    print("主")

""" # -- 屏幕输出结果如下:
主
子进程 is running.
子进程 is done.
"""
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
26
27
28
# 执行过程

在这里,a.py文件中task函数体的代码归子进程执行; 其它代码归父进程执行!!

[父进程的启动]
    pychram里显示的a.py文件代码在硬盘里放着呢,右键点击运行,OS接收指令,先开辟一个内存空间;
    接着,OS将a.py文件的代码从硬盘读入开辟的空间,OS再调用CPU执行代码.. 该a.py文件就运行起来啦!!
将这一个过程进行抽象,抽象的结果就叫做进程!!

[子进程的启动]
    执行a.py文件里的 p.start() 代码, 父进程会给OS发信号, 告知它需要启动一个子进程!
注意:信号发完后,这行代码父进程就执行完毕了!!
    OS会申请新的内存空间放子进程的代码,还会拷贝一份父进程的namespace放进去.
即父进程的所有资源都会被子进程继承,包括运行父进程的进程,如pycharm.exe/cmd.exe!!
注意: 子进程只会将 a.py 中 if __name__ == '__main__': 之上的代码通过 导入的方式 拷贝一份..
OS吭哧吭哧造子进程的过程中,父进程也在运行.. 子进程造完后,父子进程并发执行.

父进行运行最后一行打印 print("主") 的时候, 子进程还没有造完呢..
所以会看到终端先打印的"主"... 提醒一哈,父进程与子进程共用的同一个输出终端..

# 注意事项

开启子进程的操作 p.start() 应该写到 if __name__ == '__main__': 下面!!
这样使得该操作在导入该操作所在模块时不被运行. 即这个if语句中的语句将不会在导入时被调用!!!

具体来说, 在windows上,开启子进程时,子进程拷贝父进程空间的方式比较特殊.
它会将父进程对应的文件当作模块重新导一遍, 将导入的成果放到子进程自己的内存里..
我们晓得, 执行文件不等于导入文件 ; 导入模块会运行文件、产生namespace、丢入一堆数据.
意味着,一旦执行代码 p.start() 开启子进程, 该代码又将被执行一遍,无限套娃/无限开启子进程!!
Ps: 我试了下,在mac上也需要放到那下面,centos则不用..Hhh 为了兼容性,还是得放.

# 继承Process类

自定义的类一定要继承Process, 并且自定义类里一定要有一个名为 run 的绑定方法!!

敲黑板! 把子进程开启后需要执行的代码放到绑定方法run里!!!

# 实现代码
import time
from multiprocessing import Process


class Myprocess(Process):
    def __init__(self, x):
        super().__init__()
        self.x = x

    def run(self):
        print(f"{self.x} is running.")
        time.sleep(3)
        print(f"{self.x} is done.")


if __name__ == '__main__':
    p = Myprocess('子进程')
    p.start()  # -- 内部就是在调用run方法! 你问为啥?那就需要看源码的调用关系了.
    print("主")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 与第一种方式的区别

直接使用默认的Process类实例化对象调用start创建子进程, 是通用的造进程的方式 !!
可以造不同的子进程执行不同的任务, 类实例化对象时指定不同的target参数值就可以啦!!

p1 = Process(target=task1, args=('子进程1',))
p2 = Process(target=task2, args=('子进程2',))
p3 = Process(target=task3, args=('子进程3',))
p1.start()
p2.start()
p3.start()
1
2
3
4
5
6

而自定义类继承Process的方式,相当于只能造一个特定的子进程.. (Because只能执行run函数..)


# 僵尸进程与孤儿进程

详见 前面linux基础中"管理进程"章节相关的内容..

# 抛出问题

思考一个问题: 在创建子进程的第一种方式 "Process类实例化" 中,父进程在自己代码执行完后,并不会立马结束掉, 会等到所有子进程结束后 ,才会结束,为什么呢?

# -- a.py
import os
import time
from multiprocessing import Process

def task(x, n):
    print(f"{x} is running.")
    time.sleep(n)
    print(f"{x} is done.")

if __name__ == '__main__':
    p1 = Process(target=task, args=('子进程1', 3))
    p2 = Process(target=task, args=('子进程2', 5))
    p1.start() # -- 再次强调!信号发完后,这行代码父进程就执行完毕了!!
    p2.start() # -- 再次强调!信号发完后,这行代码父进程就执行完毕了!!
    print(p1.pid, p2.pid, os.getpid())
    print("主")
    time.sleep(50)

"""
21233 21234 21231
主
子进程2 is running.
子进程1 is running.
子进程1 is done.
子进程2 is done.
"""
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
26
27

在a.py程序运行5-50s的时间段,在cmd终端输入以下命令.. 可以看到z状态!! z为僵尸进程的标识!!!

# 理论阐述

若现在有一个父进程和两个子进程(p1和p2).

[正常情况,父进程等着所有子进程运行完后再结束] -- 僵尸进程

首先要知道,从运行角度来看,这里的父进程和两个子进程是完全独立不相干的3个进程.彼此隔离的.

父进程无法预知子进程什么时候结束,为了保证在父进程在活着的时候,可以随时查看三个子进程的状态. 设置了僵尸进程这种独特的数据结构.
僵尸进程是指 子进程在运行完毕以后 ,OS会将子进程占用的重型资源都释放掉(eg:关闭已打开的文件,舍弃已占用的cpu、内存、交换空间等), 但 是会保留部分子进程的关键状态信息(eg: PID 、退出状态、已运行时间等)
父进程把自己活干完后,没有立刻结束,是为了等着给子进程收尸, 即所有的子进程进入僵尸状态后,父进程会统一发起回收操作.具体来说,会由父进程发起一个 系统调用 wait / waitpid来通知linux操作系统来清理这些僵尸进程.

所有的子进程结束后都会进入僵尸进程的状态!!
通常我们不会等父进程结束时自动回收,会使用join提前回收这个僵尸进程..

[子进程还未运行完,父进程先嗝屁了] -- 孤儿进程

若父进程先死掉, 不会影响子进程的运行, 但还未运行完的子进程将会成为孤儿进程..
孤儿进程将被PID为1的 顶级进程init/systemd (其PID为0,是所有进程的祖宗,linux的进程是一个树形结构) 所收养,并由该顶级进程对它们完成状态收集工作..
通俗来说,父进程死的时候,否管它的子进程是处于僵尸状态还是孤儿状态,只要父进程不等所有子进程结束就嗝屁,这些子进程都将被顶级进程init/systemd托管!! 父进程死之前,已经结束变成僵尸进程的子进程在父进程死后会被init回收;父进程死后,还未运行完的进程是孤儿进程,运行完后会变成僵尸状态的孤儿进程,也将被init回收!!

[注意事项]

孤儿进程没有害,是因为有人管它;正常情况下,子进程死后会被父进程发起系统调用回收.也没有害.
坏就坏在父进程一直不死,不停的在开子进程,还不发起回收僵尸进程的信号.那么保留的信息就得不到释放.
僵尸进程越积越多,占用过多的PID号,新的软件就启动不起来! (PID是有限的!!)
解决方案: kill掉父进程,父进程在世时产生的僵死进程就会变成了处于僵死状态的孤儿进程..还未运行完的子进程会变成孤儿进程,在运行完后也会进入僵死状态.. 这些孤儿进程都会被init进程接管...

egon语录:不懂开发的运维就是秋后的蚂蚱,蹦跶不了几天.Hhh


# 进程内存空间彼此隔离

验证子进程与父进程内存空间之间是隔离的! (通过前面多道技术的讲解我们得知这隔离还是物理隔离的)
提醒一下, 说的是内存空间彼此隔离,硬盘空间是共享的.

import time
from multiprocessing import Process

x = 100


def task():
    # -- 子进程在开启时会拿到和父进程有一样的变量x和task (定义函数和定义变量是一回事)
    #    所以子进程在启动起来后能访问得到task! global x 声明的全局变量x是以子进程自己的为准!
    #    因而子进程改的全局变量是子进程里的全局变量,不会影响到父进程!!
    global x
    x = 0
    print("子进程已经运行完毕!")


if __name__ == '__main__':
    p = Process(target=task)
    p.start()  # -- 发信号
    time.sleep(3)  # -- 睡3秒,让父进程在原地等待3秒,是不想让父进程立刻运行`print(x)`这行代码.
                   #    确保OS把子进程开启并执行完.才会让父进程运行`print(x)`这行代码.
    print(x)  # 100 -- 父进程里的x仍为100,证明没有被子进程影响到!!

"""
子进程已经运行完毕!
100
"""
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
26

思考:  time.sleep(3) 3秒时间能保证子进程运行完吗?但凡子进程代码多点, 我们压根不知道子进程会运行多久!把时间改成300秒? crazy. 合理情况下,我们需要一个接口等到子进程结束就不用再等了. 使用 p.join() !!!


# 进程对象的方法和属性

上面已经接触了Process类的start方法.. 下面介绍下Process类的其它方法!!
start()、join()是必须掌握的!! 其余的了解即可!

# join方法!!

p.join() 本质跟 time.sleep(n) 一样, 但时间更精准, 不用我们自己控制!

# 实现代码

time.sleep() 让父进程原地等待,等待500s后,才执行下一行代码.
p.join() 让父进程原地等待,不影响子进程的运行, 等到子进程彻彻底底的运行完毕, 会回收子进程(确切点应称作僵尸进程)占用的pid等资源.. 再执行下一行代码.

import time
from multiprocessing import Process


def task(x):
    print(f"{x} is running!")
    time.sleep(2)
    print(f"{x} is done!")


if __name__ == '__main__':
    p = Process(target=task, args=('子进程1',))
    p.start()
    # time.sleep(500)
    p.join()
    print("主")

"""
子进程1 is running!
子进程1 is done!
主
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

★官方文档有如下的说明!!

join方法对应的源码里面有一行代码起到了发起系统调用 wait / waitpid 的作用 !!

# 举一反三

核心要点: 是父进程在等,不会影响子进程的运行!!

p1、p2、p3三个子进程是并发执行的!! 三个join是在三个start信号发出去后再执行的操作!

import time
from multiprocessing import Process


def task(x, n):
    print(f"{x} is running!")
    time.sleep(n)
    print(f"{x} is done!")


if __name__ == '__main__':
    p1 = Process(target=task, args=('子进程1', 5))
    p2 = Process(target=task, args=('子进程2', 2))
    p3 = Process(target=task, args=('子进程3', 6))

    start_time = time.time()
    # -- 依次发送3个信号,发送完信号后,对应子进程就开始执行
    p1.start()
    p2.start()
    p3.start()

    # -- 【★look here!】虽然父进程的三个join代码是依次执行的,但子进程的运行实则是并行的!!
    #    是因为父进程在等的时候,造这三个子进程的信号start早就发给OS啦.
    #    进程只要start就会在开始运行了,所以p1-p3.start()时,系统中已经有四个(一父三子)并发的进程了
    # -- 父进程等p1用了5s,但在等的过程中p2,p3也在运行
    #    运行p2.join()时,p1,p2已经执行完了(p2只需要2s),就不用等了 即该行代码没有任何的阻塞
    #    运行p3.join()时,p3已经执行了5s,还需执行1s
    #    ★ So,换一换p1、p2、p3 join的顺序,谁先谁后都一样!共运行的时间是一样的!!
    #    ★ 3个join花费的总时间仍然是耗费时间最长的那个子进程运行的时间
    print("等p1结束..")
    p1.join()
    print("等p2结束..")
    p2.join()
    print("等p3结束..")
    p3.join()
    end_time = time.time()
    print("共运行:", end_time - start_time)

"""
等p1结束..
子进程1 is running!
子进程2 is running!
子进程3 is running!
子进程2 is done!
子进程1 is done!
等p2结束..
等p3结束..
子进程3 is done!
共运行: 6.1702961921691895  # -- 6s多一点,多的这一点是OS创建进程以及切换进程的时间
"""
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

蜜汁操作: 如果分要把p1、p2、p3三个子进程变成串行呢?
(自己可以这样玩, 写程序这么写,纯纯的瞎搞! 子进程串行,还不如依次执行三个函数节省进程的开销.Hhh)

start_time = time.time()
p1.start()
print("等p1结束..")
p1.join()
p2.start()
print("等p2结束..")
p2.join()
p3.start()
print("等p3结束..")
p3.join()
end_time = time.time()
print("共运行:", end_time - start_time)


# -- 想提醒一点的是,父进程执行`p1.start()`语句.OS就开始着手于创建进程p1并开始执行.
#    父进程发完信号后,接着会执行`print("等p1结束..")`语句
#    终端屏幕上先打印"等p1结束..",再打印"子进程1 is running!"
#    是因为父进程与子进程p1是并发执行的!!!看谁先执行罢了.
"""
等p1结束..
子进程1 is running!
子进程1 is done!
等p2结束..
子进程2 is running!
子进程2 is done!
等p3结束..
子进程3 is running!
子进程3 is done!
共运行: 13.303465843200684
"""
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
26
27
28
29
30
# 代码优化
import time
from multiprocessing import Process


def task(x, n):
    print(f"{x} is running!")
    time.sleep(n)
    print(f"{x} is done!")


if __name__ == '__main__':
    # p1 = Process(target=task, args=('子进程1', 1))
    # p2 = Process(target=task, args=('子进程2', 2))
    # p3 = Process(target=task, args=('子进程3', 3))
    # p1.start()
    # p2.start()
    # p3.start()

    p_list = []  # -- 里面放的是三个进程对象
    for i in range(1, 4):
        p = Process(target=task, args=(f'子进程{i}', i))
        p_list.append(p)  # -- 注意:随着循环,p变量的指向是最后一个子进程的内存地址
                          #    所以得将进程对象放入列表进行保存
        p.start()  # -- 循环三次发送三个信号

    # p1.start()
    # p2.start()
    # p3.start()

    for p in p_list:
        p.join()
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
26
27
28
29
30
31

# pid属性

pid -- 进程id号,即进程在操作系统内的身份证号,独一无二的
paid -- 当前进程的父进程的pid..

# 子进程是py进程
import time
from multiprocessing import Process


def task(x, n):
    print(f"{x} is running!")
    time.sleep(n)
    print(f"{x} is done!")


if __name__ == '__main__':
    p1 = Process(target=task, args=('子进程1', 10))
    # print(p1.pid)  # None -- 子进程还没开始创建呢,当然没有
    p1.start()
    print(p1.pid)  # -- 18689
    print("主")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

我们在上述代码中,将子进程执行时间设置为10s. 保证子进程还在执行时, 在cmd中输入以下命令.
windows: tasklist | findstr 18689
linux: ps aux | grep 18689 | grep -v grep

# -- Mac下显示结果
One_Piece@DC的MacBook ~ % ps aux | grep 18689 | grep -v grep     
One_Piece ... ... ... /Library/Frameworks/Python.framework/Versions/3.8.../Python -c from multiprocessing.spawn...

# -- windows下显示结果
C: >  tasklist | findstr 18689
python.exe   10524   Console    1    12,792k
1
2
3
4
5
6
7

Ps: mac上显示的结果..不是很通俗易懂. 以后的程序都在Linux上运行. linux上的运行实验结果,详见前面linux基础中"管理进程"章节中僵尸进程与孤儿进程中实验验证部分的内容!!!

Q: 哇!! 子进程怎么是个python进程?
A: 教给子进程执行的是python代码,python代码离开解释器后就没有执行一说啦!
    如何运行一个python程序的?把解释器和代码依次从硬盘加载到内存,用python解释器解释代码.
    开启子进程的话,不会从硬盘加载代码,会从父进程的内存拷贝一份代码给子进程..
    不仅如此, 父进程地址空间里的python解释器的代码也会拷贝一份给子进程..

# PID查看

父进程的爹是运行该py文件的程序, 在pycharm里运行就是pycharm,在cmd里运行就是cmd!!

import os
import time
from multiprocessing import Process


def task():
    print(f"子进程里看自个儿pid: {os.getpid()}")
    print(f"子进程里看父进程pid: {os.getppid()}")
    time.sleep(10)


if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()
    print("父进程里看子进程pid:", p1.pid)
    print("父进程里看自个儿pid:", os.getpid())
    print("查看父进程它爹的pid:", os.getppid())

"""
父进程里看子进程pid: 19344
父进程里看自个儿pid: 19342
子进程里看自个儿pid: 19344
子进程里看父进程pid: 19342
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

扩展说一点,关于linux中父进程终止后的现象(有占用终端前台运行,和不占用终端的后台运行一说).. 详见linux基础中"管理进程"章节!! 看一个进程有没有被终止,用命令查看,不要被表象迷惑!!
(弄个VMware虚拟机安装个centos7来实验.. 别在windows和mac上实验,有些实验结果不一致.就很烦.想不通就不要想了,以linux上的实验结果为准!╮( ̄▽ ̄"")╭)

# 注意:join与pid

疑惑之处: p.join() 最后不是会回收僵尸进程所占用的资源嘛,那为啥join后还能打印p.pid呢?

诚然, 执行join操作会,会回收僵尸进程占用的资源(eg:pid).
但该回收操作是删除父进程namespace里p对象下面的pid属性吗? no!!
回收的是OS的资源 - OS所占用的PID ; 不会影响python程序的资源 - p对象里面pid..
Ps: 回收的僵尸进程会标记为free状态(涉及到linux文件系统中删除后的inode号)..

举个极端 的例子,如果没有僵尸进程这种机制,进程一结束,就回收所有的资源.. 可能会导致py程序里p.start()给OS发送信号后,立刻查看p.pid ,查看不到, 因为此时OS还没来得及给该子进程分配pid号.. Hhh Amazing.

import time
from multiprocessing import Process


def task():
    time.sleep(3)


if __name__ == '__main__':
    p = Process(target=task)
    p.start()  # -- 一旦执行就会给子进程申请到一个pid号
    print(p.pid)  # 20344
    p.join()
    print(p.pid)  # 20344
    print("主")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# name属性

current_process().name 查看当前进程的进程名

import time
from multiprocessing import Process, current_process


def task():
    print(f"子进程里看子进程的名字: {current_process().name}")
    time.sleep(10)


if __name__ == '__main__':
    p1 = Process(target=task, name="子进程1") # -- 不设置name属性的话,默认是Process-1
    p1.start()
    print("父进程里看父进程的名字:", p1.name)
    print(current_process().name)

"""
父进程里看父进程的名字: 子进程1
MainProcess
子进程里看子进程的名字: 子进程1
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# terminate,is_alive

import time
from multiprocessing import Process


def task(n):
    time.sleep(n)


if __name__ == '__main__':
    p1 = Process(target=task, name="子进程1", args=(2,))
    p2 = Process(target=task, name="子进程1", args=(5,))
    p1.start()  # -- 只要信号一发出,子进程就处于活着的状态
    p2.start()
    print(p1.is_alive())  # True
    print(p2.is_alive())  # True
    p1.terminate()   # -- 给OS发送SIGTERM信号,表明终止该子进程的.. 但OS啥时候终止OS说的算
                     #    几乎没有terminate()的使用场景,了解即可
    time.sleep(0.1)  # -- 0.1s的时间足够OS终止p1进程啦
    print(p1.is_alive())  # False
    # time.sleep(3)
    p2.join()  # -- p1,p2两个子进程是并发执行,p1、p2执行分别需要2s、5s,p2执行完时p1早已经执行完
    print(p1.is_alive())  # False
    print(p2.is_alive())  # False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

进程理论储备
进程开发必知

← 进程理论储备 进程开发必知→

最近更新
01
deepseek本地部署+知识库
02-17
02
实操-微信小程序
02-14
03
教学-cursor深度探讨
02-13
更多文章>
Theme by Vdoing | Copyright © 2023-2025 DC | One Piece
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式