NO.4 Python多进程
零蚀
- 多任务
- 并发:cpu进行分配时间片,看起来像并行。
- 并行:多个cpi同时工作,多个程序同时工作。
- 进程编写
启动
调度
满足条件
结束
等待条件
新建
就绪
运行
等待阻塞
死亡
import multiprocessing as mp
import os
def function():
print("加油")
print(mp_process.name)
if __name__ == '__main__':
mp_process = mp.Process(target=function)
mp_process.start()
print(mp_process.pid) # 进程id
print(mp.current_process()) # 当前进程对象
print("cpu_count:", mp.cpu_count()) # cpu数量
print("父进程id:", os.getppid())
- 参数的传递
def function(a, b, c):
print("加油", a, b, c)
print(mp_process.name)
if __name__ == '__main__':
# mp_process = mp.Process(target=function, args=(1, 2, 3))
mp_process = mp.Process(target=function, kwargs={"a": 1, "b": 2, "c": 3})
mp_process.start()
- 进程的共享
进程是不能数据通信的,也就是说不可以做到数据共享,案例如下:
num = 0
def a():
global num
for i in range(100000):
num = num + 1
print("a:", num)
def b():
global num
for i in range(100000):
num = num + 1
print("b:", num)
if __name__ == '__main__':
# mp_process = mp.Process(target=function, args=(1, 2, 3))
mp_process = mp.Process(target=a)
mp_process.start()
mp_process1 = mp.Process(target=b)
mp_process1.start()
time.sleep(3)
print("main:", num)
输出的结果为:
a: 100000
b: 100000
main: 0
- 守护线程
# 终结当前进程
mp_process.terminate()
# or
# 设置守护进程()
mp_process.daemon=True
mp_process.daemon需要在start之前,且执行到时也是干掉子进程,并且干掉了子进程里面的所有缓存资源对象。
- 消息队列
进程数据示意图:
进程1
进程2
主进程
被获取数据num
被获取数据num
数据new_num
数据new_num
数据num
添加消息队列
主进程
子进程
进程2
进程1
被获取数据num
被获取数据num
消息队列num num num num...num
数据num
数据new_num
数据new_num
- 队列的特性
queue = multiprocessing.Queue(3)
queue.put(1)
queue.put("abcd")
queue.put(["aaa","bbb"])
# 如果队列已满,阻塞程序,直到队列熟取出
# queue.put(1234)
# 拿到队头,取出队列
head = queue.get()
print(head)
head1 = queue.get()
print(head1)
head2 = queue.get()
print(head2)
如果put 或get过多会导致阻塞,致使其他代码无法继续运行。为了防止阻塞导致崩溃。
# 添加不了,报异常
queue.put_nowait("adf")
# 获取不到,报异常
queue.get_nowait()
设置超时时间,这样也可以起到报错的效果
head4 = queue.get(timeout=3)
print(head4)
其他API
queue.full() # 判满
queue.empty() # 判空(3.7+ ;添加数据需要时间,在添加的过程中还是为空)
queue.qsize() # 判size
- 进程间通信
def put_data(queue):
for i in range(10):
print("放数据:", i)
queue.put(i)
time.sleep(1)
def get_data(queue):
while True:
print("取数据:", queue.get(timeout=3))
time.sleep(1)
if __name__ == '__main__':
queue = mp.Queue()
mp1 = mp.Process(target=put_data, args=(queue,))
mp2 = mp.Process(target=get_data, args=(queue,))
mp1.start()
mp2.start()
- 进程池
def funcA(str):
time.sleep(1)
print("任务A", str)
iif __name__ == '__main__':
pool = mp.Pool(3)
for i in range(10):
# 指派任务,按照队列任务完成
pool.apply(funcA, ("fjasfj;" + str(i),))
pool.close()
阻塞当前代码,至当前进程池所有进程执行完毕。
pool.join()
在进程运行之前或之后运行相应代码。
result=pool.apply_async(funcA, ("f",))
# ....
result.wait()
- 进程池的任务队列
进程池的队列需要特殊指定的消息队列:
def write(queue):
for i in range(10):
print("写入数据",i)
queue.put(i)
time.sleep(1)
def read(queue):
while True:
print("读取数据",)
value = queue.get()
print("读取数据",value)
if __name__ == '__main__':
queue = mp.Manager().Queue(3)
pool = mp.Pool(2)
pool.apply_async(write, (queue,))
pool.apply_async(read, (queue,))
pool.close()
pool.join()
这里的线程池任务指定,需要指定为异步操作,不然会卡死在某一个apply上,由于队列大小不够。
其他API
import os
os.mkdir("path")
os.makedirs("paths")
# 遍历dir
os.listdir("path")
- 进程锁
比如多个进程写同一个文件
import multiprocessing
# 对文件file进行写入操作
# "file.txt"
def write_one(file, lock):
with lock:
with open(file, "+a") as file1:
for i in range(10):
file1.write("aaa---"+str(i)+ "---")
def write_two(file, lock):
with lock:
with open(file, "+a") as file2:
for i in range(10):
file2.write("bbb---"+str(i)+"---")
if __name__ == '__main__':
lock = multiprocessing.Lock()
path = "file.txt"
p1 = multiprocessing.Process(target=write_one, args=(path, lock))
p2 = multiprocessing.Process(target=write_two, args=(path, lock))
p1.start()
p2.start()
- fork()
fork主要存在于unix/linux下,也就是mac/linux系统系统,他调用就会产生一个子进程。windows没有fork无法运行,fork会返回两次,一个是当前进程,一个是子进程:
import os
import multiprocessing
fork_pid = os.fork()
print("pid:", fork_pid)
if fork_pid != 0:
print('我是主进程', multiprocessing.current_process().name)
else:
print('我是子进程', multiprocessing.current_process().name)
输出:
pid: 36396
我是主进程 34814
pid: 0
我是子进程 36395
- PIPE管道
主要目的还是做进程间通信,PIPE分单双工和全双工,单双工是指通信双方只能有一个发起者和一个接收者,当存在发起者存在的时候,另一方只能由对方完毕之后,才能进行通信。
半双工
def sender_process(pipe):
for i in range(10):
print("发送方——>pip2", i)
pipe.send(i)
time.sleep(1)
def receive_process(pipe):
while True:
print("接收方——>pip2", pipe.recv())
time.sleep(1)
if __name__ == '__main__':
# false 单双工, True 全双工
pipe = mp.Pipe(duplex=False)
p1 = mp.Process(target=receive_process, args=(pipe[0],))
p2 = mp.Process(target=sender_process, args=(pipe[1],))
p1.start()
p2.start()
主要的方法是pipe.send(i)和pipe.recv()