本文是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