Python 49进程池 信号量 事件 回调函数

事件 Event

from multiprocessing import Process,Event
import time
# Event e 有两种状态,一种是False 一种是True.
def processa(e):
    print("开始执行process 1 !")
    print("修改e的状态")
    # 查询 Event的状态,如果是False ,那么则在这里等待,如果状态变成了True,那么则继续执行
    e.wait()
    print("继续执行")
    # 将Event的状态 改为False
    e.clear()
def processb(e):
    print("开始执行process 2")
    time.sleep(2)
    print("修改e的状态")
    # 修改Event的状态为True,同时另一个线程则继续执行
    e.set()
    # 查看 Event的状态
    print(e.is_set())
    time.sleep(2)
    print("继续检查状态")
    # 获取状态 另一个线程将Event的状态改为了True,程序会在这里阻塞
    e.wait()

if __name__ == '__main__':
    e = Event()
    a = Process(target=processa,args=(e,))
    b = Process(target=processb,args=(e,))
    a.start()
    b.start()

Semaphore

# 限制并发数量
from multiprocessing import Process,Semaphore
import time
def processa(i,s):
    # 获取一把锁 
    s.acquire()
    print("这里是%s号"%i)
    time.sleep(2)
    # 将使用完成的锁放回去,下一个线程获得锁后才可以再次向下执行
    s.release()

if __name__ == '__main__':
    # 这里是一个计数器,设置最多每次并行执行的进程数量,可以理解为设置了锁的数量
    s = Semaphore(4)
    for i in range(10):
        a = Process(target=processa,args=(i,s))
        a.start()

进程池

开辟一个池子,在里面放东西,调用进程池里面的任务,进程执行结束还放回来,不存在重复创建重复销毁的过程,提高效率

简单用法

from multiprocessing import Process,Pool
import time

def processa(n):
    time.sleep(1)
    print(n)

if __name__ == '__main__':
    # 此参数根据CPU数量来设定
    pool = Pool(4)
    # 上面是设置了进程池最大的进程数量 下面的是要执行传的参数,前面放的是函数或类名,后面放的必须是可迭代的,类或者函数必须要有个变量接收这个参数
    pool.map(processa,range(100))

进程池和多线程 信号量 执行时间对比

from multiprocessing import Process,Pool,Semaphore
import time

def processa(n):
    for i in range(5):
        n = n + i
# 由于信号量接收参数不同所以单独一个类
def processb(n,s):
    s.acquire()
    for i in range(5):
        n = n + i
    s.release()

if __name__ == '__main__':
    # 进程池开始时间
    pool_start_time = time.time()
    # 设置进程池最大执行数量
    pool = Pool(4)
    pool.map(processa,range(100))
    # 进程池结束时间
    pool_stop_time = time.time()
    # 计算时间差
    pool_time = pool_stop_time - pool_start_time
# 多线程执行
    # 多线程开始时间
    process_start_time = time.time()
    p_list = []
    for pr in range(100):
        a = Process(target=processa,args=(pr,))
        a.start()
        p_list.append(a)
    [a.join() for a in p_list ]
    # 多线程结束时间
    process_stop_time = time.time()
    # 计算多线程执行时间差
    process_time = process_stop_time - process_start_time
# 信号量的方式
    semap_start_time = time.time()
    s = Semaphore(4)
    s_list = []
    for se in range(100):
        a = Process(target=processb,args=(se,s))
        a.start()
        s_list.append(a)
    [ a.join() for a in s_list ]
    semap_stop_time = time.time()
    # 计算信号量方式执行后时间差
    semap_time = semap_stop_time - semap_start_time
    # 输出结果
    print("进程池耗时:",pool_time,"多进程耗时",process_time,"信号量耗时",semap_time)

上段代码执行输出结果:

进程池耗时: 0.16051506996154785 多进程耗时 2.6977779865264893 信号量耗时 3.499631404876709

原因:
进程池是创建出四个进程,然后循环执行传递参数,整个执行过程中参与的进程是4个.

信号量和for循环是创建了一百个进程执行,for循环是100个进程同时执行,而信号量则是创建100个进程,每次执行4个,所以时间更久

而for循环创建了100个进程,就要有对应的100个进程的创建和回收过程,所以耗时时间长,进程池只创建了4个进程,省去了创建进程和回收进程的时间\

进程池的同步方法和异步方法

同步方法
from multiprocessing import process,Pool
import time

def processa(n):
    time.sleep(0.5)
    return n*n
if __name__ == '__main__':
    pool = Pool(4)
    for i in range(10):
        # 同步方法: 进程池在此处变成了顺序执行(串行),同时可以接收一个返回值
        return_key = pool.apply(processa,args=(i,))
        print("返回值:%s"%return_key)

执行之后可以看到依次输出了循环数字的平方,0.5秒一次

异步执行
from multiprocessing import process,Pool
import time

def processa(n):
    time.sleep(0.5)
    print(n*n)
if __name__ == '__main__':
    pool = Pool(4)
    for i in range(10):
        return_key = pool.apply_async(processa,args=(i,))
        print(return_key)
    print("主进程即将结束!")
    # 主进程如果直接结束,那么进程池也将结束,所有子进程也就结束了,所以要有一个等待时间
    time.sleep(4)
    print("主进程执行结束")

执行后,十个任务瞬间创立,主进程执行进入sleep 4,十个任务进入线程池等待可用线程执行,如果主进程没有等待直接结束,那么任务也被直接结束掉
执行后输出的内容,如:

<multiprocessing.pool.ApplyResult object at 0x00000274DF554E20>

这个叫做结果对象.通过结果对象去取运行返回的结果

from multiprocessing import process,Pool
import time

def processa(n):
    time.sleep(0.5)
    return n * n
if __name__ == '__main__':
    pool = Pool(4)
    for i in range(10):
        return_key = pool.apply_async(processa,args=(i,))
        #print(return_key)
        proce_return = return_key.get()
        print("通过结果对象获取",proce_return)
    print("主进程即将结束!")
    # 主进程如果直接结束,那么进程池也将结束,所有子进程也就结束了,所以要有一个等待时间
    time.sleep(4)
    print("主进程执行结束")

执行上段代码可以看到,十个任务依次输出,变成了同步方法.
执行到proce_return = return_key.get() 这段代码时,这段代码会等待上一条执行的返回结果,返回后才会继续执行

优化:不再for循环中获取结果,而是将其加到一个列表中,通过循环列表获取结果

from multiprocessing import process,Pool
import time

def processa(n):
    time.sleep(0.5)
    return n * n
proce_list = []
if __name__ == '__main__':
    pool = Pool(4)
    for i in range(10):
        return_key = pool.apply_async(processa,args=(i,))
        proce_list.append(return_key)
    print("开始输出返回结果")
    for i in proce_list:
        print(i.get())
    print("主进程执行结束")

执行后可以看到,输出结果是每次输出4个然后卡顿一会,再次输出.
原因:提交任务是有序的,所以列表中也是有序的,但是执行时是无序的,所以在获取的时候如果有值直接输出,没有则等待.
如果前四个个最先执行,那么输出后等待,如果前四个是最后执行的,那么后面的都执行完毕了,并且有了结果,程序等待后将直接输出.

主进程等待进程池所有进程执行完毕 然后打印结果.
但是查看的时候,进程池可能还会有新的任务,主进程不能一直等待,所以要先冻结进程池,不让任务在交给进程池,同时等待进程池中任务执行完毕查看结果.
扩大时间和进程数:

from multiprocessing import process,Pool
import time

def processa(n):
    time.sleep(0.5)
    return n
proce_list = []
if __name__ == '__main__':
    pool = Pool(5)
    for i in range(100):
        return_key = pool.apply_async(processa,args=(i,))
        proce_list.append(return_key)
    # 冻结进程池,不允许接收新任务 不允许接收新任务,但是要等待现有的任务执行完成
    pool.close()
    # 等待进程池中所有排队任务执行结束
    pool.join()
    print("开始输出返回结果")
    for i in proce_list:
        print(i.get())
    print("主进程执行结束")

回调函数

from multiprocessing import Process,Pool

def process1(a):
    print("第一个函数 >" ,a)
    return a*a
def process2(b):
    print("第二个函数 >",b)
if __name__ == '__main__':
    pool = Pool()
    # callback 将第一个任务的返回值丢给这个变量的任务
    return_key = pool.apply_async(process1,args=(3,),callback=process2)
    print(return_key.get())
    print("结束")

回调函数是在主进程下执行的,PID和主进程PID相同.

你可能感兴趣的:(Python,多进程)