Python多进程

多进程

由于GIL(全局解释锁)的问题,多线程并不能充分利用多核处理器,如果是一个CPU计算型的任务,应该使用多进程模块 multiprocessing 。它的工作方式与线程库完全不同,但是两种库的语法和接口却非常相似。multiprocessing给每个进程赋予单独的Python解释器,这样就规避了全局解释锁所带来的问题。

import time
import multiprocessing
from multiprocessing.pool import Pool
from multiprocessing.dummy import Pool as ThreadPool

def profile(func):
    def wrapper(*args, **kwargs):
        import time
        start = time.time()
        func(*args, **kwargs)
        end = time.time()
        print('COST: {}'.format(end - start))
    return wrapper

def fib(n):
    if n <= 2:
        return 1
    return fib(n - 1) + fib(n - 2)

@profile
def nomultiprocess():
    fib(35)
    fib(35)

@profile
def hasmultiprocess():
    jobs = []
    for i in range(2):
        p = multiprocessing.Process(target=fib, args=(35,))
        p.start()
        jobs.append(p)

    for job in jobs:
        job.join()

if __name__ == '__main__':
    nomultiprocess()
    hasmultiprocess()

进程池

from multiprocessing import Pool
pool = Pool(2)  #默认启动的进程/线程数都为CPU数,如果python获取不到CPU数则默认为1
result=pool.map(fib, [35] * 2)

或者:
result=[]
for n in [35,35]:
    result.append(pool.apply(fib, n) )

线程池

如果分不清任务是CPU密集型还是IO密集型,就用如下2个方法分别试:

from multiprocessing import Pool
from multiprocessing.dummy import Pool

哪个速度快就用那个。从此以后尽量在写兼容的方式,这样在多线程/多进程之间切换非常方便。

如果一个任务拿不准是CPU密集还是I/O密集型,且没有其它不能选择多进程方式的因素,现在都统一直接用多进程。

进程间通信

Queue

多线程有Queue模块实现队列,多进程模块也包含了Queue类,它是线程和进程安全的。

进程的Queue类并不支持task_done和join方法,需要使用特别的JoinableQueue。

import random
import time
from multiprocessing import Process, Queue, JoinableQueue


def double(n):
    return n * 2


def producer(in_queue):
    while True:
        wt = random.random()
        in_queue.put((double, wt))
        print('produce: ', wt)
        time.sleep(wt)

        if wt > 0.9:
            in_queue.put(None)
            print('stop producer')
            break


def consumer(in_queue, out_queue):
    while True:
        task = in_queue.get()
        if task is None:
            break

        func, arg = task
        result = func(arg)
        print('consumer result: ', result)

        out_queue.put(result)
        in_queue.task_done()


if __name__ == '__main__':
    tasks_queue = JoinableQueue()  # 进程类的Queue不支持join与task_done方法
    results_queue = Queue()
    processs = []

    p = Process(target=producer, args=(tasks_queue,))
    p.start()
    processs.append(p)

    p = Process(target=consumer, args=(tasks_queue, results_queue))
    p.start()
    processs.append(p)

    tasks_queue.join()

    for p in processs:
        p.join()

    while results_queue.qsize() > 0:
        print(results_queue.get())

生产者已经不会持续的生产任务了,如果随机到的结果大于0.9就会给任务队列tasks_queue put一个None,然后把循环结束掉

消费者如果收到一个值为None的任务,就结束,否则执行从tasks_queue获取的任务,并把结果put进results_queue

生产者和消费者都结束后(有join方法保证),从results_queue挨个获取执行结果并打印出来

Pipe

Pipe返回的是管道2边的对象:「父连接」和「子连接」

def f(conn):
    conn.send(['hello'])
    conn.close()

parent_conn, child_conn = Pipe()

if __name__ == '__main__':
    p = Process(target=f, args=(child_conn,))
    p.start()
    p.join()

    print(parent_conn.recv())

当子连接发送一个带有hello字符串的列表,父连接就会收到,所以 parent_conn.recv()就会打印出来。

posted @ 2017-05-08 17:18  寻_FIND  阅读(226)  评论(0编辑  收藏  举报