多进程

本文是windows系统,使用的是跨平台的multiprocessing模块。python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。
multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。
Lock:用来避免访问冲突、
Semaphore:用来控制对共享资源的访问数量、
Event:用来实现进程间同步通信

1 process
创建进程的类:Process([group [, target [, name [, args [, kwargs]]]]]),target表示调用对象,args表示调用对象的位置参数元组。kwargs表示调用对象的字典。name为别名。group实质上不使用。
方法:is_alive()、join([timeout])、run()、start()、terminate()。
start()启动某个进程。
is_alive():当前是否存在运行的进程
join([timeout]):可以等待子进程结束后再继续往下运行,通常用于进程间的同步
run():
terminate():终止程序

属性:authkey、daemon(要通过start()设置)、exitcode(进程在运行时为None、如果为–N,表示被信号N结束)、name、pid。daemon:是父进程终止后自动终止,且自己不能产生新进程,必须在start()之前设置。

单进程

#! /user/bin/env python
#encoding=utf-8
__author__ = 'chw'
import time
import multiprocessing
def worker(num):
    n=3
    while n>0:
        print ("The time is {0}".format(time.ctime()))
        time.sleep(num)
        n=n-1
if __name__=="__main__":
    p=multiprocessing.Process(target=worker,args=(3,))
    p.start()
    print p.is_alive()
    print p.pid
    print p.name

结果

True
4128
Process-1
The time is Thu May 04 20:18:31 2017
The time is Thu May 04 20:18:34 2017
The time is Thu May 04 20:18:37 2017

多进程

import time
import multiprocessing
def work1(num):
    print 'work1'
    time.sleep(num)
    print 'endwork1'
def work2(num):
    print 'work2'
    time.sleep(num)
    print 'endwork2'
if __name__=="__main__":
    p1=multiprocessing.Process(target=work1,args=(2,))
    p2=multiprocessing.Process(target=work2,args=(3,))
    p1.start()
    p2.start()
    print p1.name+'\t'+str(p1.pid)
    print p2.name + '\t' + str(p2.pid)

结果

Process-1   5460
Process-2   4596
work1
work2
endwork1
endwork2

daemon+join() 比较
因子进程设置了daemon属性,主进程结束,它们就随着结束了。
加daemon

import time
import multiprocessing
def work1(num):
    print 'work1'
    time.sleep(num)
    print 'endwork1'
def work2(num):
    print 'work2'
    time.sleep(num)
    print 'endwork2'
if __name__=="__main__":
    p1=multiprocessing.Process(target=work1,args=(2,))
    p2=multiprocessing.Process(target=work2,args=(3,))
    p1.daemon=True
    p2.daemon=True
    p1.start()
    p2.start()
    print p1.name+'\t'+str(p1.pid)
    print p2.name + '\t' + str(p2.pid)

结果

Process-1   5460
Process-2   4596

多线程的为不加daemon

daemon+join()

#! /user/bin/env python
#encoding=utf-8
__author__ = 'chw'
import time
import multiprocessing
def work1(num):
    print 'work1'
    time.sleep(num)
    print 'endwork1'
def work2(num):
    print 'work2'
    time.sleep(num)
    print 'endwork2'
if __name__=="__main__":
    p1=multiprocessing.Process(target=work1,args=(2,))
    p2=multiprocessing.Process(target=work2,args=(3,))
    p1.daemon=True
    p2.daemon=True
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print p1.name+'\t'+str(p1.pid)
    print p2.name + '\t' + str(p2.pid)

结果

work1
work2
endwork1
endwork2
Process-1   1016
Process-2   3468

2 lock()
当多个进程需要访问共享资源的时候,Lock可以用来避免访问的冲突。

#! /user/bin/env python
#encoding=utf-8
__author__ = 'chw'
import multiprocessing
import sys
# with语句的目的在于从流程图中把try、except、 finally关键字和资源分配、释放相关代码统统去掉,
# with 语句包裹起来的代码块,在执行语句体之前会调用上下文管
# 理器的 __enter__() 方法,执行完语句体之后会执行 __exit__() 方法。
def work_with(lock,f):
    with lock:
        fs=open(f,'a+')
        n=3
        while n>1:
            fs.write('Lockd acquired via with\n')
            n=n-1
        fs.close()
def work_no_with(lock,f):
    lock.acquire()
    try:
        fs=open(f,'a+')
        n=3
        while n>1:
            fs.write('Lock acquired directly\n')
            n=n-1
        fs.close()
    finally:
        lock.release()
if __name__=="__main__":
    lock=multiprocessing.Lock()
    f = "file.txt"
    p1=multiprocessing.Process(target=work_with,args=(lock,f,))
    p2=multiprocessing.Process(target=work_no_with,args=(lock,f,))
    p1.start()
    p2.start()
    # p1.join()
    # p2.join()
    print 'end'

结果

Lockd acquired via with
Lockd acquired via with
Lock acquired directly
Lock acquired directly

3. Semaphore
Semaphore用来控制对共享资源的访问数量,例如池的最大连接数。
Semaphore(2):表示访问最大2个进程
Semaphore(3):表示访问最大3个进程

#! /user/bin/env python
#encoding=utf-8
__author__ = 'chw'
import multiprocessing
import time

def work(s,i):
    s.acquire()
    print multiprocessing.current_process().name + "acquire";
    time.sleep(i)
    print multiprocessing.current_process().name + "release\n";
    s.release()
if __name__=="__main__":
    s=multiprocessing.Semaphore(2)
    for i in range(5):
        p=multiprocessing.Process(target=work,args=(s,i*2,))
        p.start()

结果

Process-1acquire
Process-1release

Process-2acquire
Process-5acquire
Process-2release

Process-3acquire
Process-3release

Process-4acquire
Process-5release

Process-4release

4 Event
Event用来实现进程间同步通信。

#! /user/bin/env python
#encoding=utf-8
__author__ = 'chw'
import multiprocessing
import time
def wait_for_event(e):
    print("wait_for_event:starting")
    # 一直等待到变化
    e.wait()
    print ("wait_for_event:e.is_set->"+str(e.is_set()))

def wait_for_event_timeout(e,t):
    print("wait_for_event_timeout:starting")
    e.wait(t)
    print ("wait_for_event_timeout:e.is_set->" + str(e.is_set()))
if __name__=="__main__":
    e=multiprocessing.Event()
    w1=multiprocessing.Process(name="block",target=wait_for_event,args=(e,))
    w2=multiprocessing.Process(name="non_block",target=wait_for_event_timeout,args=(e,2,))
    w1.start()
    w2.start()
    time.sleep(10)
    e.set()
    print ("main:event is set")

结果

wait_for_event:starting
wait_for_event_timeout:starting
wait_for_event_timeout:e.is_set->False
main:event is set
wait_for_event:e.is_set->True

5. Queue
Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。

get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常。Queue的一段示例代码:

#! /user/bin/env python
#encoding=utf-8
__author__ = 'chw'
import multiprocessing
import time
def write(q):
    try:
        s=[1,2,3]
        q.put(s,block=False)
    except:
        pass
def read(q):
    try:
        print q.get(block=False)
    except:
        pass
if __name__=="__main__":
    q=multiprocessing.Queue()
    writer=multiprocessing.Process(target=write,args=(q,))
    writer.start()

    reader=multiprocessing.Process(target=read,args=(q,))
    reader.start()
    reader.join()
    writer.join()

结果

[1, 2, 3]

6. Pipe
Pipe的本质是进程之间的数据传递,而不是数据共享,这和socket有点像。
Pipe方法返回(conn1, conn2)代表一个管道的两个端。Pipe方法有duplex参数,如果duplex参数为True(默认值),那么这个管道是全双工模式,也就是说conn1和conn2均可收发。duplex为False,conn1只负责接受消息,conn2只负责发送消息。
send和recv方法分别是发送和接受消息的方法。例如,在全双工模式下,可以调用conn1.send发送消息,conn1.recv接收消息。如果没有消息可接收,recv方法会一直阻塞。如果管道已经被关闭,那么recv方法会抛出EOFError。如果两个进程试图在同一时间的同一端进行读取和写入那么,这可能会损坏管道中的数据。
默认情况下:全双工 Pipe[0]为发送 Pipe[1]为接受

#! /user/bin/env python
#encoding=utf-8
__author__ = 'chw'
import multiprocessing
import time
def proc1(pipe):
    while True:
        for i in xrange(10):
            print "send: %s" %(i)
            pipe.send(i)
            time.sleep(1)
def proc2(pipe):
    while True:
        print "proc2.rev:", pipe.recv()
        time.sleep(1)
def proc3(pipe):
    while True:
        print "proc3.rev:",pipe.recv()
        time.sleep(1)
if __name__=="__main__":
    pipe=multiprocessing.Pipe()

    p1=multiprocessing.Process(target=proc1,args=(pipe[0],))
    p2=multiprocessing.Process(target=proc2,args=(pipe[1],))
    # p3=multiprocessing.Process(target=proc3,args=(pipe,))

    p1.start()
    p2.start()
    # p3.start()

    p1.join()
    p2.join()
    # p3.join()

结果:

send: 0
proc2.rev: 0
send: 1
proc2.rev: 1
send: 2
proc2.rev: 2

7. Pool
Pool可以提供指定数量的进程供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来它。
默认情况下:进程个数与cpu的个数相同

进程池中常用方法:
apply() 同步执行(串行) 阻塞
apply_async() 异步执行(并行)非阻塞
terminate() 立刻关闭进程池
join() 主进程等待所有子进程执行完毕。必须在close或terminate()之后。
close() 等待所有进程结束后,才关闭进程池。
map()

apply_async例子

#! /user/bin/env python
#encoding=utf-8
__author__ = 'chw'
import multiprocessing
import time
def fun(msg):
    print "msg:",msg
    time.sleep(3)
    print "end"
if __name__=="__main__":
    pool=multiprocessing.Pool(processes=3)
    for i in xrange(5):
        msg="hello %d" %(i)
        pool.apply_async(fun,args=(msg,))
    print "Mark~Mark"
    pool.close()
    pool.join()
    print "Sub-process(es) done"

结果:

Mark~Mark
msg: hello 0
msg: hello 1
msg: hello 2
end
msg: hello 3
end
msg: hello 4
end
end
end
Sub-process(es) done

apply例子

#! /user/bin/env python
#encoding=utf-8
__author__ = 'chw'
import multiprocessing
import time
def fun(msg):
    print "msg:",msg
    time.sleep(3)
    print "end"
if __name__=="__main__":
    pool=multiprocessing.Pool(processes=3)
    for i in xrange(5):
        msg="hello %d" %(i)
        pool.apply(fun,args=(msg,))
    print "Mark~Mark"
    pool.close()
    pool.join()
    print "Sub-process(es) done"

结果

msg: hello 0
end
msg: hello 1
end
msg: hello 2
end
msg: hello 3
end
msg: hello 4
end
Mark~Mark
Sub-process(es) done

map()
map(func, iterable[, chunksize=None])
Pool类中的map方法,与内置的map函数用法行为基本一致,它会使进程阻塞直到返回结果。
注意,虽然第二个参数是一个迭代器,但在实际使用中,必须在整个队列都就绪后,程序才会运行子进程。

import multiprocessing
import time

def func_with_return(msg):
    print "*msg: ", msg
    time.sleep(3)
    print "*end"
    return "{} return".format(msg)

if __name__ == "__main__":
    # 维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
    pool = multiprocessing.Pool(processes=3)
    results = []
    msgs = []
    for i in range(10):
        msg = "hello [{}]".format(i)
        msgs.append(msg)

    results = pool.map(func_with_return, msgs)

    print "--" * 10
    pool.close()   # 关闭pool, 则不会有新的进程添加进去
    pool.join()    # 必须在join之前close, 然后join等待pool中所有的线程执行完毕
    print "All process done."

    print "Return results: "
    for i in results:
        print i   # 获得进程的执行结果

结果

*msg:  hello [0]
*msg:  hello [1]
*msg:  hello [2]
*end*end

*msg:  hello [3]
*msg:  hello [4]
*end
*msg:  hello [5]
*end*end

*msg:  hello [6]
*msg:  hello [7]
*end
*msg:  hello [8]
*end
*end
*msg:  hello [9]
*end
*end
--------------------
All process done.
Return results: 
hello [0] return
hello [1] return
hello [2] return
hello [3] return
hello [4] return
hello [5] return
hello [6] return
hello [7] return
hello [8] return
hello [9] return

Process finished with exit code 0

8 Manager
通过Manager可实现进程间数据的共享。Manager()返回的manager对象会通过一个服务进程,来使其他进程通过代理的方式操作python对象。manager对象支持 list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value ,Array.
from multiprocessing import Process, Manager

#! /user/bin/env python
#encoding=utf-8
__author__ = 'chw'
import multiprocessing
import time
def f(d,l):
    d[1]='1'
    d['2']=2
    d[0.25]=None
    l.append(1)
    print l
if __name__=="__main__":
    manager=multiprocessing.Manager()
    with manager:
        d=manager.dict()
        l=manager.list(range(5))
        p_list=[]
        for i in range(3):
            p=multiprocessing.Process(target=f,args=(d,l,))
            p.start()
            p.join()
        print d
        # print l

结果

[0, 1, 2, 3, 4, 1]
[0, 1, 2, 3, 4, 1, 1]
[0, 1, 2, 3, 4, 1, 1, 1]
{0.25: None, 1: '1', '2': 2}

待定http://www.cnblogs.com/huxi/archive/2010/06/26/1765808.html#3629588
http://www.cnblogs.com/whatisfantasy/p/6440585.html
http://www.2cto.com/kf/201510/446160.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值