python多进程,进程池及进程间通信

python 多任务之进程

进程:

一个程序运行起来后, 代码 + 用到的资源称为进程, 它是操作系统分配资源的基本单元. 进程也可以完成多任务

进程的状态

工作中, 任务数往往大于 CPU 核数, 即一定有一些任务正在执行, 而另一些任务在等待 CPU 执行, 因此导师了进程有了不同的状态

  • 就绪态: 运行的条件都已经满足, 正在等待 CPU 执行
  • 执行态: CPU 正在执行其功能
  • 等待泰: 等待某些条件满足 如 time.sleep()

进程的创建 - multiprocessing

multiprocessing 模块是跨平台版本的多进程模块, 提供了一个Process类来代表一个进程对象, 这个对象可以理解为是一个独立的进程, 可以执行另外的事情

# –*– coding: utf-8 –*–
# @Time      : 2019/3/19 22:18
# @Author    : Damon_duanlei
# @FileName  : process_test.py
# @BlogsAddr : https://blog.csdn.net/Damon_duanlei

import multiprocessing
import time


def func_1():
    while True:
        time.sleep(1)
        print("1")


def func_2():
    while True:
        time.sleep(1)
        print(2)


if __name__ == '__main__':
    p1 = multiprocessing.Process(target=func_1)
    p2 = multiprocessing.Process(target=func_2)
    p1.start()
    p2.start()

给自进程的函数传参

# –*– coding: utf-8 –*–
# @Time      : 2019/3/19 22:18
# @Author    : Damon_duanlei
# @FileName  : process_test.py
# @BlogsAddr : https://blog.csdn.net/Damon_duanlei

import multiprocessing
import time


def func_1(num):
    while True:
        time.sleep(1)
        print(num)


def func_2(num):
    while True:
        time.sleep(1)
        print(num)


if __name__ == '__main__':
    p1 = multiprocessing.Process(target=func_1, args=(1,))
    p2 = multiprocessing.Process(target=func_2, args=(2,))
    p1.start()
    p2.start()

进程间共享代码不共享资源(代码写时拷贝)

创建子进程时, 系统在内存中开辟一块新的空间将主进程中所有的资源复制一份到子进程中, 子进程运行过程中变量值的改变不影响主进程, 主进程中的全局变量也不共享.

from multiprocessing import Process
import os
import time
nums = [11, 22]
def work1():
"""⼦进程要执⾏的代码"""
print("in process1 pid=%d ,nums=%s" % (os.getpid(), nums))
for i in range(3):
nums.append(i)
time.sleep(1)
print("in process1 pid=%d ,nums=%s" % (os.getpid(), nums))
def work2():
"""⼦进程要执⾏的代码"""
print("in process2 pid=%d ,nums=%s" % (os.getpid(), nums))
if __name__ == '__main__':
p1 = Process(target=work1)
p1.start()
p1.join()
p2 = Process(target=work2)
p2.start()

*进程间通信

Process 之间有时需要通信, 操作系统提供了很多机制来实现进程间的通信

1.Queue的使用

可以使用multiprocessing 模块的 Queue实现过个进程之间的数据传递, Queue本身是一个消息队列程序.

初始化Queue() 对象时 (如: q = Queue()), 若括号中没有指定最大可接受的消息数量, 或数量为负值, 那么就代表可接受的消息数量没有上限.

  • Queue.qsize(): 返回当前队列包含的消息数量

  • Queue.empty() : 如果队列为空, 返回 True , 反之返回 False

  • Queue.full() : 如果队列满返回True, 反之返回 False

  • Queue.get(block[,timeout]) : 获取队列中的第一条消息, 然后将其从队列中移除, block 默认值为 True

    1 )如果block 使用默认值, 且没有设置timeout, 消息队列如果为空,此时程序将被阻塞, 知道从消息队列读到消息为止, 如果设置了 timeout, 则会等到 timeout秒, 若还没读取到任何消息, 则抛出 "Queue.Empty"异常

    1. 如果 block 值为 False. 如果消息队列为空, 则会立刻抛出 "Queue.Empty"异常
  • Queue.get_nowait() : 相当于 Queue.get(block=False)

  • Queue.put(item,[block[,timeout]]) : 将item消息,写入对列, block默认值为True

    1. 如果block 使用默认值, 且没有设置 timeout , 消息队列如果已经没有空间可以写入, 此时程序将被阻塞, 直到从消息队列腾出空间为止, 如果设置了 timeout , 则会等到 timeout秒, 若还没空间, 则抛出 "Queue.Full"异常

    2. 如果block 值为False, 消息队列如果没有空间可写入, 则会立刻抛出 "Queue.Full"异常

  • Queue.put_nowait(item) : 相当 Queue.put(item, False)

###2.Manager()

Python中进程间共享数据,处理基本的queue,pipe和value + array外,还提供了更高层次的封装。使用multiprocessing.Manager可以简单地使用这些高级接口。 Manager()返回的manager对象控制了一个server进程,此进程包含的python对象可以被其他的进程通过proxies来访问。从而达到多进程间数据通信且安全。Manager支持的类型list,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value和Array。

共享列表:

from multiprocessing import Manager,Process
def foo(l,i):
    l.append(i**i)
if __name__ == '__main__':
    man=Manager()
    ml=man.list([11,22,33])
    l=[]
    for i in range(5):
        p=Process(target=foo,args=(ml,i))
        p.start()
        l.append(p)
    for i in l: #必须要join,不然会执行报错,处理一个数据必须要一个个来,不能同时处理一个数据
        i.join()
    print(ml)

共享字典:

from multiprocessing import Manager,Process
def foo(d,k,v):
    d[k]=v
if __name__ == '__main__':
    man=Manager()
    md=man.dict({'name':'bob'})
    l=[]
    for i in range(5):
        p=Process(target=foo,args=(md,i,'a'))
        p.start()
        l.append(p)
    for i in l: #必须要join,不然会执行报错,处理一个数据必须要一个个来,不能同时处理一个数据
        i.join()
    print(md)

进程池 Pool

当需要创建的进程数量不多时, 可以直接利用 multiprocessing 中的 Process动态生成多个进程, 但如果是上百个甚至上千个目标, 手动的去创建进程的工作量巨大, 此时就可以用到 multiprocessing 模块提供的Pool方法

# –*– coding: utf-8 –*–
# @Time      : 2019/3/24 14:03
# @Author    : Damon_duanlei
# @FileName  : pool_test.py
# @BlogsAddr : https://blog.csdn.net/Damon_duanlei

from multiprocessing import Pool
import os, time, random


def worker(msg):
    t_start = time.time()
    print("{}开始执行,进程号为{}".format(msg, os.getpid()))
    # random.random()随机⽣成0~1之间的浮点数
    time.sleep(random.random() * 2)
    t_stop = time.time()
    print(msg, "执行完毕,耗时%0.2f" % (t_stop - t_start))


if __name__ == '__main__':
    # 定义一个进程池
    po = Pool(3)
    for i in range(0, 10):
        # Pool().apply_async(要调用的目标, 传入参数为元组)
        # 每次循环将会用空闲出来的子进程去调用目标
        po.apply_async(worker, (i,))

    print("----start----")
    # 关闭进程池,关闭后po不再接收新的请求
    po.close()
    # 等待po中所有⼦进程执⾏完成,必须放在close语句之后
    po.join()
    print("-----end-----")

执行结果:

----start----
0开始执行,进程号为6500
1开始执行,进程号为6404
2开始执行,进程号为14512
1 执行完毕,耗时0.18
3开始执行,进程号为6404
0 执行完毕,耗时1.16
4开始执行,进程号为6500
2 执行完毕,耗时1.28
5开始执行,进程号为14512
3 执行完毕,耗时1.26
6开始执行,进程号为6404
6 执行完毕,耗时0.53
7开始执行,进程号为6404
5 执行完毕,耗时0.74
8开始执行,进程号为14512
8 执行完毕,耗时0.51
9开始执行,进程号为14512
9 执行完毕,耗时0.14
4 执行完毕,耗时1.57
7 执行完毕,耗时0.86
-----end-----

进程池中的Queue

如果要使用 Pool 创建进程, 就需要使用 multiprocessing.Manager() 中的 Queue(),而不是 multiprocessing.Queue(), 否则会出现错误信息.

# –*– coding: utf-8 –*–
# @Time      : 2019/3/24 14:03
# @Author    : Damon_duanlei
# @FileName  : pool_test.py
# @BlogsAddr : https://blog.csdn.net/Damon_duanlei

from multiprocessing import Manager, Pool
import os, time, random


def reader(q):
    print("reader启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
    for i in range(q.qsize()):
        print("reader从Queue获取到消息:%s" % q.get(True))


def writer(q):
    print("writer启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
    for i in "hello world":
        q.put(i)
        
        
if __name__ == "__main__":
    print("(%s) start" % os.getpid())
    q = Manager().Queue()  # 使用Manager中的Queue
    po = Pool()
    po.apply_async(writer, (q,))
    time.sleep(1)  # 先让上面的任务向Queue存入数据,然后再让下面的任务开始从中取数据
    po.apply_async(reader, (q,))
    po.close()
    po.join()
    print("(%s) End" % os.getpid())

运行结果:

(15408) start
writer启动(16076),父进程为(15408)
reader启动(3088),父进程为(15408)
reader从Queue获取到消息:h
reader从Queue获取到消息:e
reader从Queue获取到消息:l
reader从Queue获取到消息:l
reader从Queue获取到消息:o
reader从Queue获取到消息: 
reader从Queue获取到消息:w
reader从Queue获取到消息:o
reader从Queue获取到消息:r
reader从Queue获取到消息:l
reader从Queue获取到消息:d
(15408) End

你可能感兴趣的:(python,多任务,多进程,进程间通信)