内容大纲:
一、用python开启进程的两种方式
二、join控制子进程
三、daemon守护进程
四、Lock锁
五、Semaphore信号量
六、Event事件
一、用python开启进程的两种方式
multiprocessing包:是python中一个用于操作、管理进程的包,这个包几乎包含了进程有关的所有的子模块。大致可分为四个部分:创建进程部分、进程同步部分、进程池部分、进程之间数据共享部分。
multiprocessing.Process模块是专门用来创建进程的模块
1、直接使用Process类创建一个进程
import os import time from multiprocessing import Process def func(): for i in range(10): time.sleep(0.5) print('子进程',os.getpid(),os.getppid()) if __name__ == '__main__': print('主进程',os.getpid(),os.getppid()) p = Process(target=func) # 实例化一个进程对象 p.start() # 子进程开始 for i in range(10): print('*'*i)
注意:为什么要用if __name__ == '__main__':?
在windows操作系统中由于没有fork(linux操作系统在创建进程的机制),在创建子进程的时候会自动import启动它的这个文件,而在import的时候又执行了整个文件。因此如果将process()直接写在文件中就会无限递归子进程报错。所以必须把创建子进程的部分使用if __name__ == '__main__'判断保护起来,import的时候就不会递归运行了。
主进程与子进程之间的关系:
① 父进程和子进程的启动是异步的
② 父进程在执行完毕后不会立即结束程序,而是会等所有的子进程结束,父进程负责回收子进程的资源
# 创建多个子进程和给子进程传参 import os from multiprocessing import Process def func(index): print('子进程%s:'%index,os.getpid(),os.getppid()) if __name__ == '__main__': for i in range(10): # 创建多个子进程 p = Process(target=func,args=(i,)) # 给子进程传参 p.start() print('主进程:',os.getpid(),os.getppid())
#主进程与子进程之间数据是隔离的 from multiprocessing import Process count = 100 def func(): global count count -= 1 print('子进程:',count) if __name__ == '__main__': print('主进程:',count) p = Process(target=func) p.start() 结果: 99 100
2、用自定义类开启子进程
① 自定义一个类
② 继承Process
③ 一定要实现run()方法
④ 要传参数时,首先调用Proces中的init()方法才能进行传参
import os from multiprocessing import Process class Myprosess(Process): def __init__(self,arg): super().__init__() self.arg = arg def run(self): print('子进程%s:'% self.arg,os.getpid(),os.getppid()) if __name__ == '__main__': for i in range(10): p = Myprosess(i) p.start() print('主进程:',os.getpid(),os.getppid())
二、join控制子进程
join的作用:主进程的任务执行到某个阶段,需要等待子进程执行完毕后才能继续执行,否则就进入阻塞状态。没有join的时候,子进程和主进程的发生顺序是不可与预测的。
例如:模拟发邮件
import time import random from multiprocessing import Process def func(index): time.sleep(random.random()) print('第%s邮件已发送'%index) if __name__ == '__main__': p_lst = [] for i in range(10): p = Process(target=func,args=(i,)) p.start() p_lst.append(p) for p in p_lst: p.join() print('10封邮件已全部发送完毕')
三、deamon守护进程
将一个子进程设置为守护进程之后
1、守护进程会随着主进程代码的执行完毕而结束
2、如果主进程的代码已经执行完,但是还有一些子进程还没执行完,主进程会等待子进程结束,守护进程直接结束
import time from multiprocessing import Process def func1(): count = 1 while True: time.sleep(0.5) print(count*'*') count += 1 def func2(): print('func2 start') time.sleep(5) print('func2 end') if __name__ == '__main__': p = Process(target=func1) p.daemon = True # 在start之前,将p设置为守护进程 p.start() p2 = Process(target=func2) p2.start() time.sleep(3) print('主进程') #结果:只打印了5次* func2 start * ** *** **** ***** 主进程 func2 end
四、Lock 互斥锁
Lock是一个类,在使用的时候要进行导入,和Process一样
Lock的作用:在异步的情况下,多个进程又可能修改同一份资源,就给这个修改的过程加上锁。
加锁降低了程序的效率,让原来能够同时执行的代码变成顺序执行,但是保证了数据的安全。
例如:火车票抢票
import json import time from multiprocessing import Process,Lock def search(person): with open('ticket') as f: dic = json.load(f) time.sleep(0.2) # 模拟网络延迟 print('%s进行余票查询:'%person,dic['count']) def get_ticket(person): with open('ticket') as f: dic = json.load(f) time.sleep(0.2) if dic['count'] > 0: dic['count'] -= 1 print('%s买到票了'%person) time.sleep(0.2) with open('ticket','w') as f: json.dump(dic,f) else: print('%s没买到票'%person) def main(person,lock): search(person) lock.acquire() get_ticket(person) lock.release() if __name__ == '__main__': lock = Lock() for i in range(10): p = Process(target=main,args=(i,lock)) p.start() #结果是: 1进行余票查询: 6 2进行余票查询: 6 3进行余票查询: 6 7进行余票查询: 6 0进行余票查询: 6 6进行余票查询: 6 4进行余票查询: 6 9进行余票查询: 6 5进行余票查询: 6 8进行余票查询: 6 1买到票了 2买到票了 3买到票了 7买到票了 0买到票了 6买到票了 4没买到票 9没买到票 5没买到票 8没买到票
五、Semaphore信号量
信号量的作用:互斥锁Lock只允许一个线程更改数据,而信号量Semaphore同时允许一定数量的线程更改数据
信号量是计数器+锁实现的
例如:ktv允许同一时间4个人唱歌
import random import time from multiprocessing import Process,Semaphore def ktv(person,sem): sem.acquire() print('