守护进程(p.daemon = True)将p设置为守护进程
注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止(主进程和子进程是异步的),当主进程停止,该守护进程不在继续执行.守护进程也是一种子进程.
主进程创建守护进程
其一:守护进程会在主进程代码执行结束后就终止.(但本质上是在主进程结束之前结束的,主进程需要负责回收资源)
其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children
from multiprocessing import Process
import time
import os
def func(num):
print(f'{num},pid:{os.getpid()},ppid:{os.getppid()}')
while True:
print('is alive')
time.sleep(0.5)
def wahaha():
i = 0
while i lt; 5:
i += 1
print(f'第{i}秒')
time.sleep(1)
if __name__ == '__main__':
Process(target=wahaha).start() #子进程在主进程结束后仍然正常执行
p = Process(target=func,args=(1,))
p.daemon = True #主进程结束,该守护进程结束,将p设置为守护进程
p.start()
time.sleep(3)
print(f'pid:{os.getpid()},ppid:{os.getppid()}')
print('主进程结束')
执行结果:
第1秒
1,pid:8200,ppid:2000
is alive
is alive
第2秒
is alive
is alive
第3秒
is alive
is alive
pid:2000,ppid:7244
主进程结束
第4秒
第5秒
创建守护进程
多进程中的方法
p = Process(target=func,args=(1,)) #创建一个进程对象
p.start() 启动一个进程
p.daemon = True 设置进程为守护进程,随主进程结束而结束.
p.is_alive() 判断进程是否存活,返回bool值
p.terminate() 发送给操作系统指令,关闭进程
p.pid()查看进程pid
另一种执行/编写方式
from multiprocessing import Process
import time
import os
def func(num):
print(f'{num},pid:{os.getpid()},ppid:{os.getppid()}')
while True:
print('is alive')
time.sleep(0.5)
def wahaha():
i = 0
while i < 10:
i += 1
print(f'第{i}秒')#f表示print的格式化输出,待查询
time.sleep(1)
if __name__ == '__main__':
p2 = Process(target=wahaha)
p2.start()
p = Process(target=func,args=(1,))
p.daemon = True #主进程结束,该子进程结束
p.start()
time.sleep(3)
print(p.is_alive())
print(p2.is_alive())
p2.terminate()
time.sleep(0.1)
print(p.is_alive())
print(p2.is_alive())
print(f'pid:{os.getpid()},ppid:{os.getppid()}')
print('主进程结束')
p2.join()
Process方法//看不懂下面
import socket
from multiprocessing import Process
def talk(conn,addr):
while True:
msg_r = conn.recv(1024).decode('utf-8')
print(addr,msg_r)
msg_s = 'client{}登陆'.format(addr)
conn.send(msg_s.encode('utf-8'))
conn.close()
if __name__ == '__main__':
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind(('127.0.0.1',8091))
sk.listen(5)
try:
while True:
conn,addr = sk.accept()
Process(target=talk,args=(conn,addr)).start()
finally:
sk.close()
socket多进程server
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',8091))
while True:
msg_s = input('请输入内容:')
sk.send(msg_s.encode('utf-8'))
msg_r = sk.recv(1024).decode('utf-8')
print(msg_r)
sk.close()
socket多进程client
进程同步(multiprocessing.Lock、Semaphore、Event)
锁 multiprocessing.Lock()
避免同一段代码被多个进程同时执行
lock = Lock() 创建锁对象
lock.acquire() 查询钥匙,如果有就拿走,如果没有就等待
lock.release() 归还钥匙
lock可以使用with上下文进行管理(类似于文件读取)//未理解
with lock:
print('hello' )
维护数据的安全
降低了程序的效率
所有的效率都是建立在数据安全的角度上的
但凡涉及到并发编程都要考虑数据的安全性
我们需要在并发部分对数据修改的操作格外小心,如果会涉及到数据的不安全,就需要进行加锁控制
lock 内部实现了进程之间的通信,使得谁acquire了谁release了能够在多个拥有lock参数的子进程中透明(类似于PV操作)
from multiprocessing import Lock
lock = Lock() #创建一个锁对象
lock.acquire() #想拿钥匙,如果有就拿,没有就一直等
print('拿到要钥匙了1')
lock.release() #还钥匙
lock.acquire() #想拿钥匙
print('拿到要钥匙了2')
lock.release() #还钥匙
开启一个进程锁
#db文件内容 {"count": 0}
import json
import time
from multiprocessing import Process,Lock
def search(i):
f =open('db')
ticket_dic =json.load(f)
f.close()
print(f"{i} 正在查票,剩余票数{ticket_dic['count']}")
def buy(i):
with open('db') as f: ticket_dic = json.load(f)
time.sleep(0.2)
if ticket_dic['count'] > 0:
ticket_dic['count'] -= 1
print(f'{i} 买到票了')
time.sleep(0.2)
with open('db','w') as f :json.dump(ticket_dic,f)
else:
print(f"{i} 太火爆被抢购一空了,剩余票数{ticket_dic['count']}")
# def get_ticket(i,lock):
# search(i)
# lock.acquire()
# buy(i)
# lock.release()
def get_ticket(i,lock):
search(i)
with lock:
buy(i)
if __name__ == '__main__':
lock = Lock()
for i in range(10):
p = Process(target=get_ticket,args=(i,lock))
p.start()
火车票查询购买
信号量(标志TrueFalse) multiprocessing.Semaphore() (锁+计数器)
有多个钥匙的锁
互斥锁同时只允许一个线程更改数据,而信号量Semaphore是同时允许一定数量的线程更改数据 。
假设商场里有4个迷你唱吧,所以同时可以进去4个人,如果来了第五个人就要在外面等待,等到有人出来才能再进去玩。
实现:
信号量同步基于内部计数器,每调用一次acquire(),计数器减1;每调用一次release(),计数器加1.当计数器为0时,acquire()调用被阻塞。信号量概念P()和V()的Python实现。信号量同步机制适用于访问像服务器这样的有限资源。
信号量与进程池的概念很像,但是要区分开,信号量涉及到加锁的概念
信号量
sem = Semaphore(4) 创建锁对象,4把钥匙,可以被连续acquire4次
sem.acquire() 查询钥匙,如果有就拿走,如果没有就等待
sem.release() 归还钥匙
sem 可以使用with上下文进行管理(类似于文件读取)with sem:#待测试
print('hello' )
"""
可能相当于在代码块前后分别加了acquire和release
"""
with sem:#待测试
print('hello' )
"""
可能相当于在代码块前后分别加了acquire和release
"""
from multiprocessing import Semaphore
sem = Semaphore(4) #4把钥匙
sem.acquire()#1
print(1)
sem.acquire()#2
print(2)
sem.release()#1
sem.acquire()#2
print(3)
sem.acquire()#3
print(4)
sem.acquire()#4
print(5)
sem.acquire()#5
print(6)
from multiprocessing import Semaphore,Process
import time
import random
# def ktv(sem,i):#方法一
# sem.acquire()
# print(f'{i}走进ktv')
# time.sleep(random.randint(1,3))
# print(f'{i}走出ktv')
# sem.release()
def ktv(sem,i):#方法二
with sem:
print(f'{i}走进ktv')
time.sleep(random.randint(1,3))
print(f'{i}走出ktv')
if __name__ == '__main__':
sem = Semaphore(4)
for i in range(10):
p = Process(target=ktv,args=(sem,i))
p.start()
事件 multiprocessing.Event()
控制子进程执行还是阻塞的一个机制
e = Event() 创建一个事件对象
Event方法在事件中有一个信号(标志)
wait() 如果这个标志是True,那么wait的执行效果就是pass ,如果是False,那么wait方法的效果就是阻塞,直到这个标志变成True。
控制标志方法
is_set() 判断标志的状态,返回bool值
set() 将标志设置为True
clear() 将标志设置为False
from multiprocessing import Event
e = Event() #阻塞,事件的创建之初标志的状态是False
print(e.is_set())
e.set() #将标志改为True
print(e.is_set())
e.wait() #当标志为True是pass,不阻塞
创建一个事件
from multiprocessing import Event,Process
import time
def func1(e):
print('start func1')
print(e.is_set()) #事件创建之初是False
e.wait(1) #不修改状态(网络测试,发送短信,发送邮件),超时后继续执行,不继续阻塞
print(e.is_set())
e.wait() #持续阻塞
print(e.is_set()) #主进程3(异步)s后修改信号标志为True ,继续执行
print('end func1')
if __name__ == '__main__':
e = Event()
Process(target=func1,args=(e,)).start()
time.sleep(3)
e.set()
事件的控制
from multiprocessing import Event,Process
import time
import random
def tarffic_light(e):
while True:
while e.is_set():
print('绿灯亮')
time.sleep(2)
e.clear()
else:
print('红灯亮')
time.sleep(2)
e.set()
def car(i,e):
while not e.is_set():
print(f'{i}正在等待通过...')
e.wait()
else:
print(f'{i}通过.')
if __name__ == '__main__':
e = Event()
light = Process(target=tarffic_light,args=(e,))
light.daemon =True
light.start()
car_list = []
for i in range(1,21):
p = Process(target=car,args=(i,e))
car_list.append(p)
p.start()
time.sleep(random.randint(0,3))
for i2 in car_list:i2.join() #控制子进程先执行完毕
print('执行完啦')
while版红绿灯
import time
import random
from multiprocessing import Process,Event
def traffic_light(e):
print('红灯亮')
while True:
time.sleep(2)
if e.is_set():
print('红灯亮')
e.clear()
else:
print('绿灯亮')
e.set()
def car(i,e):
if not e.is_set():
print('car%s正在等在通过'%i)
e.wait()
print('car%s通过'%i)
if __name__ == '__main__':
e = Event()
light = Process(target=traffic_light,args=(e,))
light.daemon = True
light.start()
car_lst = []
for i in range(20):
p = Process(target=car,args=(i,e))
p.start()
time.sleep(random.randint(0,3))
car_lst.append(p)
for car in car_lst:car.join()
if 红绿灯
说明:红绿灯的的变化和汽车的通行是两个独立的进程,汽车通过对红绿灯的事件信号的查询判断等待和放行,每一个汽车都是独立的进程
进程间通信(进程之间数据共享)
队列multiprocessing.Queue (先进先出)队列是基于(管道+锁)实现的
创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。
Queue([maxsize])
创建共享的进程队列。
参数 :maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。
底层队列使用管道和锁定实现。
q =Queue() 创建一个队列 q = Queue(5) 队列长度为5
q.put(1) 向队列中放一个数据,可以是int,list,dict …当队列满时会阻塞
q.get() 从队列中获取一个数据 没有值会一直阻塞
q.empty() 判断队列是否为空返回bool值 多进程时不准 ,如果其他进程或线程正在往队列中添加项目,结果是不可靠的。也就是说,在返回和使用结果之间,队列中可能已经加入新的项目。注重进程访问控制
q.full() 判断队列是否已满返回bool值 多进程时不准由于线程的存在,结果也可能是不可靠的。注重进程访问控制
q.qsize()返回队列中目前项目的正确数量。此函数的结果并不可靠,因为在返回结果和在稍后程序中使用结果之间,队列中可能添加或删除了项目。在某些系统上,此方法可能引发NotImplementedError异常。注重进程访问控制
q.close()关闭队列,防止队列中加入更多数据。调用此方法时,后台线程将继续写入那些已入队列但尚未写入的数据,但将在此方法完成时马上关闭。如果q被垃圾收集,将自动调用此方法。关闭队列不会在队列使用者中生成任何类型的数据结束信号或异常。例如,如果某个使用者正被阻塞在get()操作上,关闭生产者中的队列不会导致get()方法返回错误。
q.cancel_join_thread()不会再进程退出时自动连接后台线程。这可以防止join_thread()方法阻塞。
q.join_thread()连接队列的后台线程。此方法用于在调用q.close()方法后,等待所有队列项被消耗。默认情况下,此方法由不是q的原始创建者的所有进程调用。调用q.cancel_join_thread()方法可以禁止这种行为。
q = Queue(3)
try:
q.get_nowait()#就算被锁了,也往外拿出,除非empty
except:
print('队列中没有值')
q.get_nowait()
q = Queue(3)
q.put(1)
q.put('aaa')
q.put([1,2,3])
# q.put('alex') #队列满会阻塞,就会一直卡在那里,对单进程丝毫没有体现作用,需要多进程,另一个进程get一个数据,此命令才可能执行
try:
q.put_nowait('alex')#就算被锁了,也往里加入,除非full
except:
print('丢失了一个数据')
q.put_nowait()
创建一个队列
from multiprocessing import Process,Queue
def func(num,q):
q.put({num:num**num})
if __name__ == '__main__':
q = Queue()
# p = Process(target=func, args=(10,q))
# p.start()
# print(q.get())
for i in range(10):
p = Process(target=func,args=(i,q))
p.start()
for i in range(10):
print(q.get())
=============
{0: 1}
{1: 1}
{3: 27}
{4: 256}
{2: 4}
{5: 3125}
{9: 387420489}
{8: 16777216}
{6: 46656}
{7: 823543}
使用Queue队列特性使用put,get解决返回值问题