并发编程之进程基础
一、join() 方法
阻塞当前进程,直到调用join方法的那个进程执行完,再继续执行当前进程
控制进程是否结束,进程结束就结束阻塞
使进程处于阻塞状态,直到进程执行完就结束阻塞
相当于同步控制
from multiprocessing import Process
def sendmail(argv):
print('快递%d已送达'%argv)
if __name__ == '__main__':
li = []
for i in range(10):
p = Process(target=sendmail,args=(i,)) # 创建子进程对象
p.start() # 启动子进程
li.append(p) # 将对象假如到列表
for p in li: # 循环对象的列表
p.join() # 使进程处于阻塞状态,直到进程执行完就结束阻塞
print('10件快件已发送完毕')
'''
结果
快递1已送达
快递0已送达
快递2已送达
快递4已送达
快递3已送达
快递5已送达
快递6已送达
快递8已送达
快递7已送达
快递9已送达
10件快件已发送完毕
'''
二、第二种开启子进程的方式
面向对象的方式启动,创建类且继承Process方法,并重写run方法
start()是开启子进程,让子进程执行run方法
注意:多个进程不能同时往一个文件写,造成资源抢占,令指针混乱
from multiprocessing import Process
import os
class Myprogress(Process): # 继承rocess类
def run(self): # 重写run方法
print('子进程:%d,父进程:%d'%(os.getpid(),os.getppid()))
if __name__ == '__main__':
for i in range(5):
p = Myprogress() # 创建对象
p.start() # 开启进程
print('主进程:%d'%os.getpid())
'''
结果
主进程:484
子进程:8752,父进程:484
子进程:11244,父进程:484
子进程:9804,父进程:484
子进程:12792,父进程:484
子进程:6868,父进程:484
'''
传参数给子进程
from multiprocessing import Process
class Mycourse(Process):
def __init__(self,argv): # 初始化方法
super().__init__() # 继承父类的初始化方法
self.argv = argv # 创建一个变量,用于传参
def run(self,): # 启动进程执行的方法
print('子进程%d号'% self.argv)
if __name__ == '__main__':
for i in range(1,10,2):
p = Mycourse(i)
p.start()
print('主进程')
'''
结果
主进程
子进程3号
子进程1号
子进程5号
子进程7号
子进程9号
'''
第二种方式开启子进程并加入join方法
from multiprocessing import Process
class Mycourse(Process):
def __init__(self,argv): # 初始化方法
super().__init__() # 继承父类的初始化方法
self.argv = argv # 创建一个变量,用于传参
def run(self,): # 启动进程执行的方法
print('子进程%d号'% self.argv)
if __name__ == '__main__':
pro_lis = []
for i in range(1,10,2):
p = Mycourse(i)
p.start()
pro_lis.append(p)
for p in pro_lis:
p.join() # 阻塞当前进程,直到调用join方法的那个进程执行完,再继续执行当前进程
print('主进程')
'''
结果
子进程1号
子进程7号
子进程3号
子进程5号
子进程9号
主进程
'''
三、守护进程
p.daemon = True 设置p进程为守护进程,必须在start方法之前完成
守护进程会随着主进程的代码执行完毕而结束
主进程代码已经执行完,但是子进程还没执行完,守护进程都不会继续执行。
子进程要在主进程结束前结束
主进程会等待子进程结束,守护进程之等待主进程代码结束就结束了
守护进程的作用:程序的报活
from multiprocessing import Process
import time
def func():
for i in range(1,10):
print('子进程%d号'% i)
time.sleep(1)
if __name__ == '__main__':
p = Process(target=func)
p.daemon = True # 设置进程为守护进程
p.start()
time.sleep(3)
print('主进程结束')
'''
结果
子进程1号
子进程2号
子进程3号
主进程结束
'''
四、其他方法
p.terminate()#关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活
p.pid 查看子进程的pid
from multiprocessing import Process
import time
import os
def func():
for i in range(1,10):
time.sleep(1)
print('%d秒' % i,'进程号:',os.getpid())
if __name__ == '__main__':
p = Process(target=func)
p.start()
print(p.pid) # 查看子进程的pid号
time.sleep(3)
p.terminate() # 关闭子进程进程,但不是马上关闭
print(p.is_alive()) # 查看子进程的状态
time.sleep(1)
print(p.is_alive()) # 查看子进程的状态
'''
结果
10720
1秒 进程号: 10720
2秒 进程号: 10720
True
False
'''
五、锁(互斥锁)
步骤:
实例化锁对象,把锁对象传给子进程
锁对象.acquire() 给代码上锁
锁对象.release() 释放锁
上锁为了保证数据的安全,在异步的情况下,多个进程有可能同时修改同一份资源,就给修改的过程加上锁
加锁降低了程序的效率,让原来能够同时执行的代码变成了顺序执行了,异步变同步的过程,保证了数据的安全
from multiprocessing import Process
from multiprocessing import Lock
import json
def get_ticket(name,lock):
lock.acquire()
with open('ticket') as f:
dic = json.load(f)
if dic['count'] > 0:
with open('ticket','w') as f1:
dic['count'] -= 1
json.dump(dic,f1)
print('%s已买票'% name)
else:print('%s走路回家'% name)
lock.release()
if __name__ == '__main__':
name_lis = ['小明','小红','小蓝','小黑','小白','小青','小小','小树']
lock = Lock() # 创建锁对象
for i in range(8):
p = Process(target=get_ticket,args=(name_lis[i],lock))
p.start()
'''
小明已买票
小红已买票
小蓝已买票
小黑已买票
小白已买票
小青走路回家
小小走路回家
小树走路回家
'''
死锁:在同一个级才能中,连续acquire()两次或以上,程序就不能继续执行,造成了死锁现象
六、信号量
锁的变形,多个锁,使部分进程可以执行代码
Semaphore信号量,保证资源不会被多进程执行
使用方法跟锁类似
信号量的实现机制:用计算器+锁实现
from multiprocessing import Process
from multiprocessing import Semaphore
import time
def get_ticket(name,sem):
sem.acquire()
print('%s 打饭'% name)
time.sleep(2)
print('%s 离开' % name)
sem.release()
if __name__ == '__main__':
name_lis = ['小明','小红','小蓝','小黑','小白','小青','小小','小树']
sem = Semaphore(4) # 创建信号量对象
for i in range(8):
p = Process(target=get_ticket,args=(name_lis[i],sem))
p.start()
'''
结果
小明 打饭
小红 打饭
小蓝 打饭
小黑 打饭
小明 离开
小青 打饭
小红 离开
小白 打饭
小蓝 离开
小小 打饭
小黑 离开
小树 打饭
小青 离开
小白 离开
小小 离开
小树 离开
'''
七、事件
Event 模块
方法: wait()
wait是否阻塞使看event对象内部的一个属性的值,默认是False
控制属性的值的方法:
set() 将属性的值改为True
clear() 将属性的值改为False
is_set() 判断当前属性的值是否为True
例子:红绿灯
from multiprocessing import Event
from multiprocessing import Process
import time
def traffic_light(e):
while 1:
if e.is_set(): #
e.clear() # 设置状态为False
print('\033[31m红灯亮\033[0m')
time.sleep(3)
else:
print('\033[32m绿灯亮\033[0m')
e.set() # 设置状态为True
time.sleep(5)
def car(e,name):
e.wait()
time.sleep(0.5)
if e.is_set():
print('%s 通行了' % name)
if __name__ == '__main__':
cat_list = ['昌河','奇瑞','东风','吉利','长城','一汽']
e = Event()
p1 = Process(target=traffic_light,args=(e,))
p1.daemon = True # 将红绿灯设置为守护进程
p1.start()
p2_lis = []
for i in range(6):
p2 = Process(target=car,args=(e,cat_list[i]))
p2.start()
p2_lis.append(p2)
time.sleep(2)
p2.join() # 设置当前进程阻塞,使子进程执行完才执行当前进程
print('车辆全部通过')
'''
绿灯亮
昌河 通行了
奇瑞 通行了
东风 通行了
红灯亮
绿灯亮
吉利 通行了
长城 通行了
一汽 通行了
车辆全部通过
'''