暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

python多进程&协程使用心得汇总

ala阿拉 2022-05-12
975

一、多进程:


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*x


          if __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() - begin
              print(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 time
              def wrapper(*args, **kwargs):
              print('开始执行')
              st = time.time()
              old_func(*args, **kwargs)
              et = time.time()
              print('结束执行')
              print('执行耗时:{}'.format(et - st))
              return wrapper
              @count_time
              def 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操作的情况下,建议使用进程类。



              文章转载自ala阿拉,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

              评论