No.32
今日概要
- 线程
- multiprocessing 模块
内容回顾
- 操作系统
- 发展历史
- 多道操作系统
- 分时操作系统
- 实时操作系统
- 功能
- 负责调度进程先后执行的顺序
- 控制进程执行的时间
- 分配资源
- 发展历史
- 进程
- 进程和程序的区别
- 运行的程序就是一个进程
- 进程的调度
- 是由操作系统完成
- 三状态
- 就绪
- 就绪 → system call → 运行
- 运行
- 运行 → 时间片到 → 就绪
- 阻塞
- 运行 → IO → 阻塞 → IO结束 → 就绪
- 阻塞影响了程序运行的效率
- 就绪
- 进程和程序的区别
- 同步异步
- 同步:调用一个方法要等待这个方法结束。
- 异步:调用一个方法不等待这个方法结束,也不关心这个方法做了什么。
- 并发并行
- 并发:
- 多个程序共用一个CPU轮流使用
- 并行:
- 多个程序,多个CPU
- 一个CPU上运行一个程序,多个程序同时在多个CPU上运行
- 并发:
- 阻塞非阻塞
- 阻塞:CPU不工作
- 非阻塞:CPU工作
内容详细
1.线程
- 线程是进程中的一部分,专门负责执行具体代码。
- 每一个进程中至少有一个线程。
- 线程和进程的区分
- 功能
- 进程是计算机中资源分配的最小单位(进程负责分配资源)
- 线程是计算机中被CPU调度的最小单位(线程负责执行代码)
- 开销
- 线程的创建、销毁、切换所需时间开销都远远小于进程。
- 总结
- 进程:数据隔离 开销大 同时执行几段代码
- 线程:数据共享 开销小 同时执行几段代码
- 功能
- Python中的线程比较特殊,所以进程也可能会被常用到。
2.multiprocessing 模块
import os
import time
print('start')
time.sleep(20)
print(os.getpid(), os.getppid(), 'end')
# pid:process id 子进程id
# ppid:parent process id 父进程id
# 在Pycharm中启动的所有Py程序都是Pycharm的子进程
import os
import time
from multiprocessing import Process
def func():
print('start', os.getpid())
time.sleep(1)
print('end', os.getpid())
if __name__ == '__main__':
P = Process(target=func) # 创建一个即将要执行func函数的进程对象
P.start() # 异步:调用开启进程的方法,不等待是否真的开启。
print('main', os.getpid())
结果:
main 12108
start 8628
end 8628
'''
if __name__ == '__main__':
当这个Py文件当作脚本直接执行的时候,会执行里面的代码。
当这个Py文件当作模块被导入的时候,不执行里面的代码。
__name__ == '__main__'
执行的文件就是__name__所在的文件。
__name__ == '文件名'
__name__所在的文件被导入执行的时候。
'''
import os
import time
from multiprocessing import Process
def func():
print('start', os.getpid())
time.sleep(1)
print('end', os.getpid())
# windows系统中不写 if __name__ == '__main__': 执行时会报错
P = Process(target=func)
P.start()
print('main', os.getpid())
# 报错原因:操作系统创建的进程方式不同
windows系统中创建新的子进程,子进程会先import父进程的代码,从而完成数据导入。
所以我们只希望在父进程中执行的代码就要写在 if __name__ == '__main__': 下面。
ios/linux系统创建新的子进程,子进程会直接拷贝父进程执行代码后的结果,不会再重新执行一遍父进程的代码。(fork)
import os
import time
from multiprocessing import Process
def func():
print('start func', os.getpid())
time.sleep(1)
print('end func', os.getpid())
def show():
print('start show', os.getpid())
time.sleep(1)
print('end show', os.getpid())
if __name__ == '__main__':
p1 = Process(target=func) # 创建一个即将要执行func函数的进程对象
p1.start() # 开启进程
p2 = Process(target=show)
p2.start()
print('main', os.getpid())
结果:
main 9652
start func 1632
start show 6728
end func 1632
end show 6728
'''
主进程的结束逻辑:
1主进程代码执行结束 → 所有子进程结束 or 所有子进程结束 → 主进程代码执行结束
2给子进程回收资源
3主进程结束
'''
import os
import time
from multiprocessing import Process
def func():
print('start', os.getpid())
time.sleep(10)
print('end', os.getpid())
if __name__ == '__main__':
P = Process(target=func)
P.start()
print('main', os.getpid())
# 主进程代码执行完后并不会立即结束。
# 主进程会等待所有子进程结束后才会结束,因为主进程要负责回收子进程的资源。
# 如果子进程结束,父进程没有回收资源,那么这个子进程会变成僵尸进程。
'''
进程之间数据相互隔离,那么主进程是怎么知道子进程结束的?
基于网路,基于文件(操作系统)。
'''
# join方法:直到子进程结束才会执行后续代码
from multiprocessing improt Process
import time
def send_email():
time.sleep(3)
print('发送了一封邮件')
if __name__ == '__main__':
p = Process(target=send_email)
p.start() # 异步非阻塞
p.join() # 同步阻塞:直到p对应的进程结束,才结束阻塞。
print('邮件发送完毕')
结果:
执行发邮件函数的子进程结束后,才会打印。
# 开启10个进程,给公司5000人发邮件;直到发送完所有邮件后,再打印:邮件发送完毕。
import time
import random
from multiprocessing improt Process
def send_mail(i):
time.sleep(random.random()) # 每个进程睡眠时间随机
print('发送了一封邮件', i)
if __name__ == '__main__':
lst = []
for i in range(10):
p = Process(target=send_mail, args=(i,)) # args传入的是元组
p.start()
l.append(p) # 把每个进程对象都加入列表
print(l)
for p in lst:
p.join() # 阻塞:直到上面的10个进程都结束
print('5000封邮件发送完毕')
3总结
-
开启一个进程
- 函数名(参数1,参数2)
- from multiprocessing import Process
- p = Process ( target=函数名,args=(参数1,参数2) )
- p.start()
-
父进程和子进程
- 父进程会等待所有的子进程结束之后才会结束
- 回收资源
-
windows 和 ios/linux 开启进程的区别
- windows
- 开启进程的代码需要放在
if __name__ == '__main__'
下- 因为新开的子进程会先把主进程中的代码从头到尾先执行一遍,这样就会导致子进程连续创建子进程。
- 开启进程的代码需要放在
- lios/linux
- 新开的子进程不会执行主进程中的代码,而是直接拷贝主进程代码执行后的结果。
- 可以理解成:新开的子进程直接执行调用的函数。
- windows
-
join方法
-
把一个进程的结束事件封装成了一个join方法。
-
执行join方法的效果就是形成阻塞,直到子进程执行结束就结束。
-
在多个子进程中使用join
from multiprocessing improt Process def 函数名(参数1,参数2) pass if __name__ == '__main__': lst = [] for i in range(10): p = Process(target=函数名,args(参数1,参数2)) p.start() lst.append(p) for i in lst: p.join() # 当所有子进程都结束后,需要执行的代码就写在这里。
-