一、多进程:
1、multiprocessing
multiprocessing.Process 无法批量开启子进程
multiprocessing.Pool 可以批量开启子进程
2、执行方式
尽管Pool和Process都可以并行执行任务,但是它们并行执行任务的方式却不同。
Pool类使用FIFO( First Input First Output 先进先出)调度将任务分配给可用处理器。它的工作方式类似于map缩减架构。它将输入映射到不同的处理器,并收集所有处理器的输出。执行代码后,它将以列表或数组的形式返回输出。它等待所有任务完成,然后返回输出。执行中的进程存储在内存中,其他未执行的进程存储在内存之外。
Process类将所有进程放入内存中,并使用FIFO策略安排执行时间。进程暂停后,它将抢占并安排新进程执行。
3、使用场景
Pool 可以批量并行执行,比如【批量报名】就可以使用Pool,最后把结果汇总
Process 适用于每个任务执行一次,对输出结果要求不高的场景
4、代码实例
Process代码(每个任务执行一次,不能批量并行执行)
# 此处传的studentid是列表,根据学生数量决定开启的进程数量# args 多个参数传递for i in range(len(studentid)):p = Process(target=batch_reg, args=(env, area, classid, studentid[i]))p.start()p.join()
Pool代码
apply_async 异步非阻塞,返回的是一个对象。所以就需要先将这些对象保存在列表中,在主进程结束之后再从这些对象中获取结果,apply_async实现了并行执行
# 可以根据cpu的数量决定开启的进程数res = []p = Pool(processes=multiprocessing.cpu_count())for i in range(4):multi = p.apply_async(batch_reg, (env, area, classid, studentid))res.append(multi)p.close()p.join()
Pool执行结果----> 进程对象,通过for循环可以从这些对象中获取执行结果
[<multiprocessing.pool.ApplyResult object at 0x7fc34e5be0d0>, <multiprocessing.pool.ApplyResult object at 0x7fc34e5be1d0>, <multiprocessing.pool.ApplyResult object at 0x7fc34e5be290>, <multiprocessing.pool.ApplyResult object at 0x7fc34e5be350>]
map()& imap()接收的是可迭代对象
def func1(x):time.sleep(1)print('-----' + str(x))return x*xif __name__ == '__main__':begin = time.time()p = Pool(10)result = p.imap(func1, [i for i in range(10)])p.close()p.join()during = time.time() - beginprint(during)
二、协程
grequests (不得不说greuests真香啊,可惜和flask运行不兼容一直报错)
for i in range(len(studentid)):datas = {"classId": classid,"studentId": studentid[i],"isAsync": False,"stagesResult": 'true'}map_alone = grequests.post(url=url, headers=headers, data=datas)map_list.append(map_alone)res = grequests.map(map_list, size=7)
运行结果和多进程的apply_async对比后没什么差别,也是批量并行执行,获取结果的方式都是一样
gevent
def yield_execFunc(x):print('______________%s' % x) # yield_clist决定协程的数量#开始协程操作def yield_start(yield_clist):task = [] # 用来存储协程# print(yield_clist)for i in range(len(yield_clist)):task.append(gevent.spawn(yield_execFunc, i))# print(task)gevent.joinall(task) # 执行协程def count_time(old_func):"""函数计时装饰器"""import timedef wrapper(*args, **kwargs):print('开始执行')st = time.time()old_func(*args, **kwargs)et = time.time()print('结束执行')print('执行耗时:{}'.format(et - st))return wrapper@count_timedef test():list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 元素个数决定开起的协程数量list2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]list3 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]process_list = [list1, list2, list3] #元素个数决定进程数量--------------------------# 异步非阻塞,批量并行的方式p = Pool(3)for plist in process_list:p.apply_async(yield_start, args=(plist,))p.close()p.join()执行耗时:0.11659598350524902--------------------------for plist in process_list:p = Process(target=yield_start, args=(plist,))p.start()p.join()执行耗时:0.01865696907043457
执行耗时对比如我们所见,Pool仅在内存中分配正在执行的进程,而Process在内存中分配所有任务,因此,当任务数较小时,我们可以使用Process类;当任务数较大时,我们可以使用Pool。在大型任务中,如果我们使用Process,可能会发生内存问题,从而引起系统干扰。Pool,由于创建它会产生开销,因此,对于较小的任务数,使用Pool会影响性能。
IO操作
IO操作 Pool以FIFO方式在可用内核之间分配进程。在每个内核上,分配的进程按顺序执行。因此,如果有很长的IO操作,它将等待IO操作完成,并且不会安排其他进程。这导致执行时间增加。Process类则挂起执行IO操作的进程并安排另一个进程。因此,在长时间的IO操作的情况下,建议使用进程类。




