Python多进程开发之如何轻松突破GIL瓶颈

Written by

in

文章目录
  • Python多线程为何没法加速CPU密集型任务?因为GIL(全局解释器锁):在主流的CPython解释器中,无论多少线程归根结底同时只有一个线程在执行Python字节码,严重影响CPU计算性能的并行。 多进程机制完美绕开了GIL:每个进程拥有自己的Python解释器和内存空间,真正多核、多CPU并行执行,CPU密集型任务大大提速!
  • Python 内置标准库 multiprocessing,API与threading非常类似,上手容易。
  • 大量任务并发时,手动管理进程很麻烦,用Pool池自动分配和收集结果,极大提升效率! from multiprocessing import Pool import os, time def square(n): print(‘计算’, n, ‘在进程’, os.getpid()) time.sleep(1) return n * n if __name__ == ‘__main__’: with Pool(processes=3) as pool: results = pool.map(square, range(5)) print(‘返回结果:’, results) 比循环创建 Process 简洁得多,还能自动分配CPU,每个子进程并发执行给定任务。
  • 每个进程独立内存,不能像多线程那样直接共享变量 多进程用队列(Queue)、管道(Pipe)或特殊共享对象实现
  • 多进程也需防止竞争条件,通过 Lock/Manager 加锁同步。 from multiprocessing import Process, Lock, Value def add(lock, counter): for _ in range(100000): with lock: counter.value += 1 if __name__ == ‘__main__’: lock = Lock() counter = Value(‘i’, 0) procs = [Process(target=add, args=(lock, counter)) for _ in range(4)] for p in procs: p.start() for p in procs: p.join() print(‘结果:’, counter.value)
  • CPU密集(数值计算/图像处理/大数据科学):用多进程“跑满CPU”突破GIL IO密集(爬虫/网络/磁盘/数据库交互):多线程即可,更省内存和切换开销 到此这篇关于Python多进程开发之如何轻松突破GIL瓶颈的文章就介绍到这了,更多相关Python GIL内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: Python中的GIL全局解释器锁多线程编程的隐患剖析 python全局解释器GIL锁机制详解 python 深入了解GIL锁详细 详解Python中的GIL(全局解释器锁)详解及解决GIL的几种方案 Python中多进程调试技巧指南 一文带你深入了解Python中的多进程编程
  • 目录
    • 1. 为啥要用多进程?先认清GIL
    • 2. Python多进程基础用法:multiprocessing库
      • 2.1 Proces介绍
      • 2.2 Hello,多进程
      • 2.3 多进程并行计算
      • 2.4 子进程类实现(面向对象)
    • 3. 进阶:进程池批量并行(Process Pool)
      • 4. 多进程间数据:通信与共享
        • 4.1 用队列 Queue 通信(推荐)
        • 4.2 共享内存变量:Value 与 Array
      • 5. 多进程安全:锁机制
        • 6. 多进程vs多线程,该如何选?

          Python多线程为何没法加速CPU密集型任务?因为GIL(全局解释器锁):在主流的CPython解释器中,无论多少线程归根结底同时只有一个线程在执行Python字节码,严重影响CPU计算性能的并行。

          多进程机制完美绕开了GIL:每个进程拥有自己的Python解释器和内存空间,真正多核、多CPU并行执行,CPU密集型任务大大提速!

          Python 内置标准库 multiprocessing,API与threading非常类似,上手容易。

          Process([group [, target [, name [, args [, kwargs]]]]])

          • target 表示调用对象
          • args 表示调用对象的位置参数元组
          • kwargs 表示调用对象的字典
          • name 为别名
          • group 实质上不使用

          from multiprocessing import Process
          import os
          
          def worker():
              print('子进程PID:', os.getpid())
          
          if __name__ == '__main__':
              p = Process(target=worker)
              p.start()
              p.join()
              print('主进程结束')
          

          注意! Windows下多进程要写在 if __name__ == '__main__': 保护块内,否则会无限递归启动进程。

          from multiprocessing import Process
          import time
          
          def task(name):
              print(f'{name} 开始')
              time.sleep(2)
              print(f'{name} 结束')
          
          if __name__ == '__main__':
              proc_list = []
              for i in range(3):
                  p = Process(target=task, args=(f'进程{i+1}',))
                  p.start()
                  proc_list.append(p)
              for p in proc_list:
                  p.join()
              print('所有子进程结束')
          

          多个进程可并发分担任务,真正多核利用!

          import multiprocessing
          import time
          
          class ClockProcess(multiprocessing.Process):
              def __init__(self, interval):
                  multiprocessing.Process.__init__(self)
                  self.interval = interval
          
              def run(self):
                  n = 5
                  while n > 0:
                      print("当前时间: {0}".format(time.ctime()))
                      time.sleep(self.interval)
                      n -= 1
          
          
          if __name__ == '__main__':
              p = ClockProcess(3)
              p.start()
          
          

          大量任务并发时,手动管理进程很麻烦,用Pool池自动分配和收集结果,极大提升效率!

          from multiprocessing import Pool
          import os, time
          
          def square(n):
              print('计算', n, '在进程', os.getpid())
              time.sleep(1)
              return n * n
          
          if __name__ == '__main__':
              with Pool(processes=3) as pool:
                  results = pool.map(square, range(5))
              print('返回结果:', results)
          

          比循环创建 Process 简洁得多,还能自动分配CPU,每个子进程并发执行给定任务。

          • 每个进程独立内存,不能像多线程那样直接共享变量
          • 多进程用队列(Queue)、管道(Pipe)或特殊共享对象实现

          from multiprocessing import Process, Queue
          
          def producer(q):
              for i in range(5):
                  q.put(i)
              q.put(None)  # 发送结束标记
          
          def consumer(q):
              while True:
                  item = q.get()
                  if item is None:
                      break
                  print('消费:', item)
          
          if __name__ == '__main__':
              q = Queue()
              p1 = Process(target=producer, args=(q,))
              p2 = Process(target=consumer, args=(q,))
              p1.start()
              p2.start()
              p1.join()
              p2.join()
          

          from multiprocessing import Process, Value, Array
          
          def worker(val, arr):
              val.value += 10
              for i in range(len(arr)):
                  arr[i] *= 2
          
          if __name__ == '__main__':
              num = Value('i', 5)                # int类型共享变量
              arr = Array('i', [1, 2, 3, 4])     # 数组
              p = Process(target=worker, args=(num, arr))
              p.start()
              p.join()
              print('num =', num.value)
              print('arr =', list(arr))
          

          多进程也需防止竞争条件,通过 Lock/Manager 加锁同步。

          from multiprocessing import Process, Lock, Value
          
          def add(lock, counter):
              for _ in range(100000):
                  with lock:
                      counter.value += 1
          
          if __name__ == '__main__':
              lock = Lock()
              counter = Value('i', 0)
              procs = [Process(target=add, args=(lock, counter)) for _ in range(4)]
              for p in procs:
                  p.start()
              for p in procs:
                  p.join()
              print('结果:', counter.value)
          

          • CPU密集(数值计算/图像处理/大数据科学):用多进程“跑满CPU”突破GIL
          • IO密集(爬虫/网络/磁盘/数据库交互):多线程即可,更省内存和切换开销

          到此这篇关于Python多进程开发之如何轻松突破GIL瓶颈的文章就介绍到这了,更多相关Python GIL内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

          您可能感兴趣的文章:

          • Python中的GIL全局解释器锁多线程编程的隐患剖析
          • python全局解释器GIL锁机制详解
          • python 深入了解GIL锁详细
          • 详解Python中的GIL(全局解释器锁)详解及解决GIL的几种方案
          • Python中多进程调试技巧指南
          • 一文带你深入了解Python中的多进程编程

          站内搜索