在之前一直有注意到python的GIL(gobal interrupt lock).然而在实际运用却常常忽略,在最近的一个face recognition的项目的中。自测的时候,好好的。然而上线的时候,因为并发量太大,导致很多请求超时。才真正的意识到了这个问题。所有有了这篇博客。
其实之前一直听过python的线程很废物,但真正了解一下,就明白了。你所做的项目或者工程是cpu密集型还是io密集型,决定了你用什么来进行编程。用进程的话,你可能你对服务器要求高一点吧。
from multiprocessing import Process
import time
import logging
def profile(func):
def wrapper(*args,**kwargs):
start=time.time()
func(*args,**kwargs)
end=time.time()
logging.info(‘Cost:%d’% (end-start))
return wrapper
def fib(n):
if n<=2:
returnt 1
return fib(n-1)+fib(n-2)
@profile
def nomultiprocess():
fib(35)
fib(35)
@profile
def hasmultiprocess():
jobs=[]
for _ in range(1):
p =Process(targs=fib,args=(35,))
p.start()
jobs.append(p)
for p in jobs:
p.join()
nomultiprocess()
hasmultiprocess()
有一点要强调:任务的执行周期决定于cpu性能和核数以及任务分配算法。更好的方法是使用Pool。
p=Pool(4)
p.map(fib,[35]*2)
好漂亮的写法,比那个apply_async好看多了,也好理解。
多进程和多线程之前怎么选择搞不清楚,偶尔会出现要从多线程改成多进程或者多进程改成多线程。
from multiprocess import Pool
from multiprocess.dummy import Pool
一般直接使用多进程
进程间的通信常用的是rpc、socket、pipe和消息队列queue。多进程模块中涉及到了后面3种。
from multiprocessing import Process,Pipe
def f(conn):
conn.send([‘hello’])
conn.close()
parent_conn,child_conn=Pipe()
p=Process(target=f,args=(child_conn,))
p.start()
print parent_conn.recv()
p.join()
其中Pipe返回的是管道2遍的对象:父连接和子连接。当子连接发送一个带有hello字符串的列表,父连接就会收到,所以parent_conn.recv()就会打印出来。这样可以简单的实现多进程之间传输python内置的数据结构了。但是先说明,不能被xmlrpclib序列化的对象是不能这么传输的。
实际的业务中通常要复杂的多,比如下面这个例子:
from multiprocess import Process,Pipe
from itertools import izip
def spawn(f):
def func(pip,item):
pip.send(f(item))
pip.close()
return func
def parmap(f,items):
pipe=[Pipe() for _ in items]
proc=[Process(target=spawn(f),args=(child,item))for item,(parent,child)in izip(items,pipe)]
[p.start()for p in proc]
[p.join() for p in proc]
return [parent.recv() for (parent,child) in pipe]
class CalculateFib(object):
...
def parmap_run(self):
print parmap(self.fib,[35]*2)
cl=CalcublateFib()
cl.parmap_run()
多线程有Queue模块实现队列,多进程模块也包含了Queue类,它是线程和进程安全的。现在我们给下面的生产者/消费者的例子添加难度,也就是用2个队列:一个队列用于存储待完成的任务,另一个用于存储任务完成后的结果。
import time
from multiprocess import Queue,JoinableQueue,Process
from random import random
results_queue=Queue()
tasks_queque=JoinableQueue()
def double(wt):
return wt*2
def producer(tasks_queque):
while 1:
wt=random.random()
time.sleep(wt)
tasks_queue.put((double,wt))
if wt>0.9:
tasks_queque.put(None)
print(“stop producer”)
break
def consumer(tasks_queue,results_queue):
while 1:
task=tasks_queue.get()
if task is None:
print (“stop consumer”)
break
func,arg=task
task.take_done()
results_queue.put(func(arg))
processes=[]
product=Process(target=producter,args=(tasks_queue,))
product.start()
processes.append(product)
consume=Process(target=consumer,args=(tasks_queue,result_queue))
consume.start()
processes.append(consume)
tasks_queue.join()
for p in processes:
p.join()
while 1:
if results_queue.empty():
break
result=results_queue.get()
print “Result”,result