# 多进程
import os
from multiprocessing import Process, current_process
# Process([group[, target[, name[, args[, kwargs]]]]])
# group分组,实际上不怎么使用
# target表示调用对象,传入任务执行函数做为参数,注意是函数名,不带括号
# args表示给调用对象以元组的形式提供 参数,两个参数时args=(m,n),一个参数时args=(m,),注意元组逗号
# kwargs表示调用对象的字典的传参
# name 是别名,相当于给这个进程取一个名字,在进程数量较多时,这个参数比较好用
# 关于进程方法的操作
# start()让进程执行target调用的对象
# join()阻塞,默认情况下,主进程不会等到子进程任务结束后主进程才结束,使用join()可以让主进程等待子进程任务结束后再结束
# terminate()结束当前,无论任务是否完成
# multiprocessing.freeze_support() 在windows上生成GUI等可执行文件时(.exe文件)时,
# 可能会无限开启新窗口或者新进程导致CPU或内存溢出,可以使用该命令解决,
# os.getpid()获取子进程id
# os.getppid()获取父进程id
# is_alive() 返回布尔值,Ture时进程开启
def test(i):
print(f"----------------子进程{os.getpid()}---------父进程{os.getppid()}---", current_process().name)
# ----------------子进程7720---------父进程10572--- 当前任务1
# ----------------子进程11556---------父进程10572--- 当前任务2
# ----------------子进程8756---------父进程10572--- 当前任务3
# ----------------子进程9400---------父进程10572--- 当前任务4
# ----------------子进程8408---------父进程10572--- 当前任务5
# if __name__ == '__main__':
# for i in range(1, 6):
# p = Process(target=test, args=(i,), name="当前任务{}".format(i))
# p.start()
# p.join() # 由于阻塞,速度会降低,但是在进程通讯方面这个方法可以使用
# 进程池
from multiprocessing import Pool, Manager
import time, random
# 部分参数
# pool.apply_async() 异步非阻塞
# pool.apply() 阻塞
# pool.join() 主进程创建或添加任务之后,默认主进程不等待子进程任务结束后再结束,
# 如果主进程任务完成后立马结束,若没有使用join()就会导致进程池中的任务不执行
def worker(msg):
start_time = time.perf_counter()
print(f"{msg}开始执行,当前的子进程是{os.getpid()}")
time.sleep(random.random() * 2)
stop_time = time.perf_counter()
print(msg, f"执行完毕,耗时{(stop_time - start_time).__round__(2)}")
def main():
pool = Pool(3) # 定义一个进程池,最大进程数为3
for i in range(0, 11):
# 每次循环将会用空闲出来的子进程调用目标
pool.apply_async(func=worker, args=(i,))
print("开始")
pool.close() # 关闭进程池,关闭后pool不再接受新的请求
pool.join() # 等待pool所有的子进程执行完成,必须放在close语句之后
print("结束")
# if __name__ == '__main__':
# main()
# 进程之间的通信
# Queue的使用
from multiprocessing import Queue, cpu_count
# Queue.qsize() 返回当前队列包含的消息的数量
# Queue.empty() 如果队列为空,返回True,条件判断
# Queue.full() 判断队列是否满来,满了返回True
# Queue.get([block[,timeout]]) 获取队列中的一条消息,然后将其从队列中移除,block默认为True
# Queue.get_nowait() 相当于Queue.get(False)
# Queue.put(item, [block[, timeout]]) 将item消息写入队列,block默认值为True
# Queue.put_nowait() 相当于Queue.put(item,Flase)
# 使用方法
# q = Queue(num) num可以为空或者num为负数时代表可接受的消息无上限
# 关键参数block,如果block使用默认值,且没有设置timeout(单位秒),,如果消息队列空了,则程序被阻塞(停在读取状态),
# 直到从消息队列读到消息位置,如果设置来timeout参数,则会等待timeout秒,若还是没有读取到消息,则抛出Queue.Empty异常;
# 如果block值为False,消息队列如果为空,就会立抛出Queue.Empty异常
# q = Queue(3) # 初始化一个Queue对象,最多可以接收三条put消息
# q.put("消息1")
# q.put("消息2")
# print(q.full())
# q.put("消息3")
# print(q.full())
# try:
# q.put("消息4", block=True, timeout=5)
# except Exception as e:
# print(f"消息队列已满,5秒后报错,现在消息队列的数量是{q.qsize()}", e)
# try:
# q.put_nowait("消息4")
# except Exception as e:
# print(f"消息队列已满,直接报错,现在消息队列的数量是{q.qsize()}", e)
# 先判读消息队列是否已满,然后再写入数据
# if not q.full():
# q.put_nowait("消息4")
# 读取消息时,先判读消息队列是否为空,再读取消息
# if not q.empty():
# for i in range(q.qsize()):
# print(f"循环读取消息队列数据{q.get_nowait()}")
# 两个进程之间的通信
# 写入队列消息
def write(q):
for value in ["A", "B", "C"]:
print(f"put {value} to queue")
q.put(value)
time.sleep(random.random())
# 读取队列消息
def read(q):
while True:
if not q.empty():
value = q.get(True)
print(f"get {value} from queue")
time.sleep(random.random())
else:
break
# if __name__ == '__main__':
# # 父进程创建Queue,并把实例q传给各个子进程
# q = Queue()
# pw = Process(target=write, args=(q,))
# pr = Process(target=read, args=(q,))
# # 启动子进程pw,写入消息
# pw.start()
# # 等待pw结束
# pw.join()
# # 启动子进程pr,读取消息
# pr.start()
# pr.join()
# print("所有消息都已经读写成功")
# 在进程池中使用Queue
# Pool创建的进程池,需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否则报错:
# RuntimeError: Queue objects should only be shared between processes through inheritance.
# 错误翻译>>>运行时错误:队列对象只能通过继承在进程之间共享。
def reader(q):
print(f"reader启动{os.getpid()},父进程为{os.getppid()}")
for i in range(q.qsize()):
print(f"reader从Queue获取到消息:{q.get(True)}")
def writer(q):
print(f"writer{os.getpid()},父进程为{os.getppid()}")
for i in "laotie":
q.put(i)
print(f"writer向Queue写入来消息:{i}")
if __name__ == '__main__':
print(f"{os.getpid()} start")
q = Manager().Queue()
pool = Pool(cpu_count())
# print(f"当前cpu的信息为{cpu_count()}", type(cpu_count())) # 当前cpu的信息为4
# 使用阻塞模式创建进程,这样就不需要在reader中使用死循环,就可以让writer执行完在执行reader
pool.apply(func=writer, args=(q,))
pool.apply(func=reader, args=(q,))
# 当进程池close的时候并未关闭进程池,只是会把状态改为不可再插入元素的状态
pool.close()
pool.join()
print(f"{os.getpid()} end")