python的进程和线程

python的进程和线程

  • python的进程和线程
    • 进程篇
      • 进程池
      • dummy
      • 基于Pipe的parmap
      • 队列

在之前一直有注意到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好看多了,也好理解。

dummy

多进程和多线程之前怎么选择搞不清楚,偶尔会出现要从多线程改成多进程或者多进程改成多线程。

from multiprocess import Pool
from multiprocess.dummy import Pool

一般直接使用多进程

基于Pipe的parmap

进程间的通信常用的是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   
  • 生产者已经不会持续的生产任务了,如果随机到的结果大于0.9就会给任务队列tasks_queue put一个None,然后把循环结束掉
  • 消费者如果搜到一个值为None的任务,就结束,否则执行从tasks_queue获取的任务,并把结果put进results_queue
  • 生产者和消费者都结束后(又join方法保证),从results_queue挨个获取执行结果并打印出来

你可能感兴趣的:(python)