1、進程簡介
io密集型的儘量使用多線程,如socketserver.
multiprocessing模塊可以使用一些簡單的API批量生生進程,可以實現本地和遠程進程間同步,通過使用子進程而不是線程來有效避開了全局解釋器鎖GIL,因此,該multiprocessing
模塊允許程序員在給定機器上充分利用多個處理器。它可以在Unix和Windows上運行。
一個簡單的例子如下:
from multiprocessing import Process
def f(name):
print 'hello', name
if __name__ == '__main__':
p = Process(target=f, args=('bob',))
p.start()
p.join()
爲了顯示所涉及的各個進程ID,下面是一個擴展的示例:
# -*- coding: utf-8 -*-
#@Author: kangdan
#@time :2019/12/12 10:42
#@Software:PyCharm Community Edition
from multiprocessing import Process
import os
def info(title):
print(title)
print('module name:', __name__)
if hasattr(os, 'getppid'): # only available on Unix
print('parent process:', os.getppid())
print('process id:', os.getpid())
def f(name):
info('function f')
print('hello', name)
if __name__ == '__main__':
info('main line')
p = Process(target=f, args=('bob',))
p.start()
p.join()
輸出:
2、進程間通訊和數據共享
不同進程間內存是不共享的,要想實現兩個進程間的數據交換,可以用以下方法:
2.1、Queue隊列
隊列是線程和進程安全的。
from multiprocessing import Process, Queue
def f(q):
q.put([42, None, 'hello'])
if __name__ == '__main__':
q = Queue()
p = Process(target=f, args=(q,))
p.start()
print(q.get()) # prints "[42, None, 'hello']"
p.join()
不可以在主程序中通過將q設置爲global方式在子進程中使用,因爲不同進程間數據是不共享的!!!
2.2、Pipe管道(不常用,一般使用Queue)
Pipe()
函數返回通過管道連接的一對連接對象,管道默認情況下爲雙工(雙向)。例如:
from multiprocessing import Process, Pipe
def f(conn):
conn.send([42, None, 'hello'])
conn.close()
if __name__ == '__main__':
parent_conn, child_conn = Pipe()
p = Process(target=f, args=(child_conn,))
p.start()
print(parent_conn.recv()) # prints "[42, None, 'hello']"
p.join()
返回的兩個連接對象Pipe()
代表管道的兩端。每個連接對象都有send()
和 recv()
方法(以及其他方法)。請注意,如果兩個進程(或線程)試圖同時從管道的同一端讀取或寫入管道的同一端,則管道中的數據可能會損壞。當然,不存在同時使用管道不同端的過程造成損壞的風險。
2.3、Managers
由返回的管理器對象Manager()
控制着一個服務器進程,該進程保存Python對象,並允許其他進程使用代理對其進行操作。
通過返回的經理Manager()
將支持類型 list
,dict
,Namespace
,Lock
, RLock
,Semaphore
,BoundedSemaphore
, Condition
,Event
,Barrier
, Queue
,Value
和Array
。例如,
from multiprocessing import Process, Manager
def f(d, l):
d[1] = '1'
d['2'] = 2
d[0.25] = None
l.reverse()
if __name__ == '__main__':
with Manager() as manager:
d = manager.dict()
l = manager.list(range(10))
p = Process(target=f, args=(d, l))
p.start()
p.join()
print(d)
print(l)
將打印
{0.25: None, 1: '1', '2': 2}
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
服務器進程管理器比使用共享內存對象更靈活,因爲可以使它們支持任意對象類型。同樣,單個管理器可以由網絡上不同計算機上的進程共享。但是,它們比使用共享內存慢。
2.4、Lock
非遞歸鎖對象:模擬threading.Lock
。一旦進程或線程獲取了鎖,則隨後從任何進程或線程獲取鎖的嘗試都將阻塞,直到釋放爲止;否則,該鎖將被釋放。任何進程或線程都可能釋放它。
請注意,Lock
實際上這是一個工廠函數,該函數返回multiprocessing.synchronize.Lock
使用默認上下文初始化的實例。
Lock
支持上下文管理器協議,因此可以在with
語句中使用。
from multiprocessing import Process, Lock
def f(l, i):
l.acquire()
try:
print('hello world', i)
finally:
l.release()
if __name__ == '__main__':
lock = Lock()
for num in range(10):
Process(target=f, args=(lock, num)).start()
2.5、Pool進程池
進程池內部維護一個進程序列,當使用時,則去進程池中獲取一個進程,如果進程池序列中沒有進程,那麼程序就會等待,直到進程池中有可用進程爲止。
進程池有兩種方法:
- apply
- apply_async
from multiprocessing import Pool,freeze_support
import time
def Foo(i):
time.sleep(2)
return i + 100
def Bar(arg):
print('-->exec done:', arg)
if __name__ == '__main__':
freeze_support()
pool = Pool(5)
for i in range(10):
pool.apply_async(func=Foo, args=(i,), callback=Bar)#異步
#callback回調函數,執行完Foo,會自動執行Bar,並且把Foo的返回值傳給Bar參數
# pool.apply(func=Foo, args=(i,))#同步,沒有callback
print('end')
pool.close()
pool.join() # 進程池中進程執行完畢後再關閉,如果註釋,那麼程序直接關閉。