[Python 多线程] multiprocessing、多进程、工作进程池 (十四)
由于Python的GIL限制,多线程未必是CPU密集型程序的好的选择。
多进程可以完全独立的进程环境中运行程序,可以充分地利用多处理器。
但是进程本身的隔离性带来的数据不共享也是一个问题。而且线程比进程轻量级。
multiprocessing
Process类
Process类遵循了Thread类的API,减少了学习难度。(几乎和Thread类使用方法一模一样)
上一篇文章里最后使用了多线程来解决CPU密集型的例子,但发现多线程和多线程最终执行效率几乎相同,多线程并没有想象中的优势。
上一篇中多线程的例子:
#模拟CPU密集型 多线程 import threading,logging,time,random,datetime DATEFMT="%H:%M:%S" FORMAT = "[%(asctime)s]\t [%(threadName)s,%(thread)d] %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT) def calc(): sum = 0 for _ in range(100000000): sum += 1 start =datetime.datetime.now() t1 = threading.Thread(target=calc) t2 = threading.Thread(target=calc) t3 = threading.Thread(target=calc) t4 = threading.Thread(target=calc) t5 = threading.Thread(target=calc) t1.start() t2.start() t3.start() t4.start() t5.start() t1.join() t2.join() t3.join() t4.join() t5.join() print('aaa') delta = (datetime.datetime.now() -start).total_seconds() print(delta) #运行结果: aaa 53.135543
此例子是单线程情况下执行耗时58秒左右。
再使用多进程的例子来看一下是否可以有所不同:
#=========多进程、真正的并行、适用于CPU计算密集型=============== import multiprocessing import datetime def calc(i): sum = 0 for _ in range(100000000): sum += 1 # print(i,sum) if __name__ == "__main__": start = datetime.datetime.now() lst = [] for i in range(5): p = multiprocessing.Process(target=calc,args=(i,),name='p-{}'.format(i)) p.start() lst.append(p) for p in lst: p.join() delta = (datetime.datetime.now() - start).total_seconds() print(delta) 运行结果: 24.767709
从耗时结果可以看出多线程的执行效率明显得要比多线程(其实就是单线程)高得多。
进程间同步:
进程间同步提供了和线程同步一样的类,使用方法一样,使用的效果也类似。
不过,进程间代价要高于线程,而且底层😡实现是不同的,只不过Python屏蔽了这些,让用户简单使用。
multiprocessing还提供共享内存、服务器进程来共享数据,还提供了Queue队列、Pipe管道用于进程间通信。
通信方式不同:
多线程就是启动多个解释器进程,进程间通信必须序列化、反序列化
数据的线程安全性问题
由于每个进程中没有实现多线程,GIL可以说没什么用了。
进程池举例:
进程池只能由创建它的进程使用
# 进程池 import multiprocessing import datetime def calc(i): sum = 0 for _ in range(100000000): sum += 1 if __name__ == "__main__": start = datetime.datetime.now() pool = multiprocessing.Pool(5) #定义同时至多起几个线程 for i in range(5): pool.apply_async(calc,args=(i,)) pool.close() pool.join() #多线程 # lst = [] # for i in range(5): # p = multiprocessing.Process(target=calc,args=(i,),name='p-{}'.format(i)) # p.start() # lst.append(p) # # for p in lst: # p.join() delta = (datetime.datetime.now() - start).total_seconds() print(delta) 运行结果: 24.944169
多进程、多线程的选择
1、CPU密集型
CPython中使用到了GIL,多线程的时候锁相互竞争,且多核优势不能发挥,Python多进程效率更高。
2、IO密集型
适合是用多线程,减少IO序列化开销。且在IO等待的时候,切换到其它线程继续执行,效率不错。
应用:
请求/应答模型:WEB应用中常见的处理模型
master启动多个worker工作进程,一般和CPU数目相同。
worker工作进程中启动多线程,提高并发处理能力。worker处理用户的请求,往往需要等待数据。
这就是nginx工作模式。