由于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工作模式。