守护进程
主进程创建守护进程(子进程)
那么主进程就是被守护进程
- 守护进程会在主进程代码执行结束后就终止
- 守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children
注意:进程之间是相互独立的,主进程代码运行结束,守护进程随即终止.
from multiprocessing import Process
import time
def task():
print('sub start')
print('sub over')
if __name__ == '__main__':
p =Process(target=task)
p.daemon = True # 将这个进程设置成子进程,必须放在p.start()前面
p.start()
print('parent start')
time.sleep(1) # 这里加延时,就是为了让子进程运行完毕,否则,就会出现子进程(守护进程)还没有运行完全,主程序(被守护进程)就已经运行完并结束代码了
print('parent over')
# 如果没有在主程序这里加延时,则不会打印子进程,因为在操作系统开启子进程时,主程序代码已经运行完毕.
parent start
sub start
sub over
parent over
进程安全问题
因为子进程的内存时相互隔离的,所以进程之间数据不共享;如果多个在子进程同时访问同一个文件,或者打印同一个终端,那么就会带来竞争,竞争带来的结果就是错乱,这个就是进程安全问题.
这里进程1和进程2,之所以把范围放这么大,是为了能显示出问题打印
from multiprocessing import Process
def task1():
for i in range(10000):
print('sub1 run')
print('sub1 over')
def task2():
for i in range(10000):
print('sub2 run')
print('sub2 over')
if __name__ == '__main__':
p1 = Process(target=task1)
p2 = Process(target=task2)
p1.start()
p2.start()
sub1 over
sub2 over
sub1 run
sub2 run
sub1 over
sub2 over
互斥锁(进程同步)
什么是互斥锁?
互相排斥的锁
原理:
就是将要操作公共资源的代码锁起来 以保证同一时间只能有一个进程在执行这部分代码
进程之间数据不共享,但是共享同一套文件系统,同时访问同一个文件,或者打印同一个终端,那么就会产生竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理
为了解决上面出现因为子进程竞争而出现的进程安全问题,为了解决这个问题,我们用到了互斥锁.
from multiprocessing import Process,Lock
def task1(mutex):
mutex.acquire()
for i in range(10000):
print('sub1 run')
print('sub1 over')
mutex.release()
def task2(mutex):
mutex.acquire()
for i in range(10000):
print('sub2 run')
print('sub2 over')
mutex.release()
if __name__ == '__main__':
mutex = Lock()
p1 = Process(target=task1,args=(mutex,))
p2 = Process(target=task2,args=(mutex,))
p1.start()
p2.start()
锁的原理
def task1():
global lock
if lock == False:
lock = True
open("aaa.txt","wt")
lock = False
def task2():
global lock
if lock == False:
lock = True
open("aaa.txt","wt")
lock = False
从这可以看出,锁并不是把数据锁住不让用,而是让代码不执行.
manager 管理器
多个子进程同时读写一个共享文件,虽然加了锁,但有时也会造成文件错乱问题,因为子进程之间数据不互通,即数据不同步.那么我们就需要创建一个同步通道
from multiprocessing import Process,Manager,Lock
import time
def task(data,lock):
lock.acquire()
num = data[0]
time.sleep(0.2)
data[0] = num -1
lock.release()
if __name__ == '__main__':
d = [100]
m = Manager() # 创建一个管理器
sync_list = m.list(d) # 让管理器创建一个进程同步的列表(也可以是字典等)
lock = Lock() # 创建一个锁
ps = []
for i in range(10):
p = Process(target=task,args=(sync_list,lock))
p.start()
ps.append(p)
for p in ps:p.join()
print(d)
print(sync_list)
sub over
sub over
sub over
sub over
sub over
sub over
sub over
sub over
sub over
sub over
[100]
[90]
总结:
加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即将并发改成串行,保证了数据安全,但是牺牲了速度.
虽然可以用文件共享数据实现进程间通信,但问题是:
- 效率低:共享数据是基于文件,而文件是在硬盘上
- 需要自己加锁处理
这种方式适合交互不频繁,数据量大的情况,
对于交互频繁,数据量小的情况不合适,因此我们用另外一种解决方案:IPC(进程间通信)--队列+管道
队列
进程间相互隔离,要实现进程间通信(IPC),multiprocess模块支持两种形式:队列和管道,这两种方式都是使用消息传递的
队列是先进先出
from multiprocessing import Queue
q = Queue(2) # 创建队列,并且同时只能存储两个元素,如果不写,默认为无限大
q.put(1) # 将数据存入管道
q.put(2)
# q.put(3,block=False,timeout=3) # block=True表示阻塞,默认为True; timeout=3表示等待延时3s,默认为None;
# 当容器中没有位置了就阻塞,等待3秒,在这个时间段有人从管道里面取走一个元素,
# 那么元素3就会存入进去,否则就会抛出错误 queue.Full
print(q.get()) # get 是将数据取出来 并且是先进先出
print(q.get())
# print(q.get(block=True,timeout=3)) # 默认是阻塞,直到有人存入元素, 跟存入一样.