记忆碎片之python多进程、进程池和消息队列

温故而知新,可以为师矣

# 多进程
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 <class 'int'>
    # 使用阻塞模式创建进程,这样就不需要在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")

发布了46 篇原创文章 · 获赞 7 · 访问量 4621

猜你喜欢

转载自blog.csdn.net/Python_DJ/article/details/105074287
今日推荐