【Python】淺談 multiprocessing
一前言
使用python進行併發處理多臺機器/多個例項的時候,我們可以使用threading ,但是由於著名的存在,實際上threading 並未提供真正有效的併發處理,要充分利用到多核CPU,我們需要使用多程式。Python提供了非常好用的多程式包--。multiprocessing 可以利用multiprocessing.Process物件來建立一個程式,該Process物件與Threading物件的用法基本相同,具有相同的方法(官方原話:"The multiprocessing package mostly replicates the API of the threading module.") 比如:start(),run(),join()的方法。multiprocessing包中也有Lock/Event/Semaphore/Condition/Pipe/Queue類用於程式之間的通訊。話不多說 show me the code!
二使用
2.1 初識異同
下面的程式顯示threading和multiprocessing的在使用方面的異同,相近的函式join(),start(),append() 等,並做同一件事情列印自己的程式pid
輸出結果
從例子的結果可以看出多執行緒threading的程式id和主程式(父程式)pid一樣 ,同為9524; 多程式列印的pid每個都不一樣,for迴圈中每建立一個process物件都年開一個程式。其他相關的方法基本類似。
2.2 用法
建立程式的類:
Process([group [, target [, name [, args [, kwargs]]]]]),
target表示呼叫物件,
args表示呼叫物件的位置引數元組。
kwargs表示呼叫物件的字典。
name為程式的別名。
group實質上不使用,為None。
方法:is_alive()、join([timeout])、run()、start()、terminate()。其中,Process以start()啟動某個程式,並自動呼叫run方法.
屬性:authkey、daemon(要透過start()設定,必須設定在方法start之前)、exitcode(程式在執行時為None、如果為–N,表示被訊號N結束)、name、pid。其中daemon是父程式終止後自動終止,且自己不能產生新程式,必須在start()之前設定。
2.3 建立單程式
單執行緒比較簡單,建立一個 Process的例項物件就好,傳入引數 target 為已經定義好的方法worker以及worker需要的引數
2.4 建立多程式
輸出
2.5 執行緒池
multiprocessing提供程式池的類--Pool,它可以指定程式最大可以呼叫的程式數量,當有新的請求提交到pool中時,如果程式池還沒有滿,那麼就會建立一個新的程式用來執行該請求;但如果程式池中的程式數已經達到規定最大值,那麼該請求就會等待,直到池中有程式結束,才會建立新的程式來它。
構造方法:
Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])
processes : 使用的工作程式的數量,如果processes是None,預設使用os.cpu_count()返回的數量。
initializer: 如果initializer是None,那麼每一個工作程式在開始的時候會呼叫initializer(*initargs)。
maxtasksperchild:工作程式退出之前可以完成的任務數,完成後用一個新的工作程式來替代原程式,來讓閒置的資源被釋放。maxtasksperchild預設是None,意味著只要Pool存在工作程式就會一直存活。
context: 用在制定工作程式啟動時的上下文,一般使用multiprocessing.Pool()或者一個context物件的Pool()方法來建立一個池,兩種方法都適當的設定了context。
例項方法:
apply(func[, args[, kwds]]):同步程式池
apply_async(func[, args[, kwds[, callback[, error_callback]]]]) :非同步程式池
close() : 關閉程式池,阻止更多的任務提交到pool,待任務完成後,工作程式會退出。
terminate() : 結束工作程式,不在處理未完成的任務.
join() : 等待工作執行緒的退出,在呼叫join()前必須呼叫close()或者 terminate(),因為被終止的程式需要被父程式呼叫wait(join等價與wait),否則程式會成為殭屍程式。
輸出結果
解釋:建立一個程式池pool 物件proc_pool,並設定程式的數量為2,xrange(4)會相繼產生四個物件[0, 1, 2, 4],四個物件被提交到pool中,因pool指定程式數為2,所以0、1會直接送到程式中執行,當其中的2個任務執行完之後才空出2程式處理物件2和3,所以會出現輸出 worker 2 worker 3 出現在end worker 0 end worker 1之後。思考一下如果呼叫 proc_pool.apply(worker, (i,)) 的輸出結果會是什麼樣的?
2.6 使用queue
multiprocessing提供佇列類,可以透過呼叫multiprocessing.Queue(maxsize) 初始化佇列物件,maxsize表示佇列裡面最多的元素個數。
例子 建立了兩個函式入隊,出隊,出隊處理時使用了lock特性,序列化取資料。
2.7 使用pipe
Pipe可以是單向(half-duplex),也可以是雙向(duplex)。我們透過mutiprocessing.Pipe(duplex=False)建立單向管道 (預設為雙向)。一個程式從PIPE一端輸入物件,然後被PIPE另一端的程式接收,單向管道只允許管道一端的程式輸入,而雙向管道則允許從兩端輸入。
用法 multiprocessing.Pipe([duplex])
該類返回一組物件例項(conn1, conn2),分別代表傳送和接受訊息的兩端。
輸出:
該例子中 p1 p2 透過pipe 給彼此相互傳送資訊,p1 傳送"parent_conn" 給 p2 ,p2 傳送"child_conn" 給p1.
2.8 daemon程式對比結果
輸出:
設定 daemon = True,程式隨著主程式結束而不等待子程式。
輸出:
end!
因為子程式設定了daemon屬性,主程式結束,multiprocessing建立的程式物件就隨著結束了。
輸出:
2.9 Lock()
當多個程式需要訪問共享資源的時候,Lock可以用來避免訪問的衝突。
例項方法:
acquire([timeout]): 使執行緒進入同步阻塞狀態,嘗試獲得鎖定。
release(): 釋放鎖。使用前執行緒必須已獲得鎖定,否則將丟擲異常。
例子:
多個程式使用同一個std_out ,使用lock機制確保同一個時刻有一個一個程式獲取輸出。
輸出:
三 小結
本文參考官方資料以及其他資源,對multiprocesssing 的使用方式做了總結,還有很多知識需要詳細閱讀官方文件。紙上來得終覺淺,絕知此事要躬行。參考資料
[1]
[2]Python標準庫10 多程式初步 (multiprocessing包)
使用python進行併發處理多臺機器/多個例項的時候,我們可以使用threading ,但是由於著名的存在,實際上threading 並未提供真正有效的併發處理,要充分利用到多核CPU,我們需要使用多程式。Python提供了非常好用的多程式包--。multiprocessing 可以利用multiprocessing.Process物件來建立一個程式,該Process物件與Threading物件的用法基本相同,具有相同的方法(官方原話:"The multiprocessing package mostly replicates the API of the threading module.") 比如:start(),run(),join()的方法。multiprocessing包中也有Lock/Event/Semaphore/Condition/Pipe/Queue類用於程式之間的通訊。話不多說 show me the code!
二使用
2.1 初識異同
下面的程式顯示threading和multiprocessing的在使用方面的異同,相近的函式join(),start(),append() 等,並做同一件事情列印自己的程式pid
-
#!/usr/bin/env python
-
# encoding: utf-8
-
import os
-
import threading
-
import multiprocessing
-
def printer(msg):
-
print(msg, os.getpid())
-
print('Main begin:', os.getpid())
-
# threading
-
record = []
-
for i in range(5):
-
thread = threading.Thread(target=printer, args=('threading',))
-
thread.start()
-
record.append(thread)
-
for thread in record:
-
thread.join()
-
# multi-process
-
record = []
-
for i in range(5):
-
process = multiprocessing.Process(target=printer, args=('multiprocessing',))
-
process.start()
-
record.append(process)
-
for process in record:
-
process.join()
- print('Main end:', os.getpid())
點選(此處)摺疊或開啟
-
Main begin: 9524
-
threading 9524
-
threading 9524
-
threading 9524
-
threading 9524
-
threading 9524
-
multiprocessing 9539
-
multiprocessing 9540
-
multiprocessing 9541
-
multiprocessing 9542
-
multiprocessing 9543
- Main end: 9524
2.2 用法
建立程式的類:
Process([group [, target [, name [, args [, kwargs]]]]]),
target表示呼叫物件,
args表示呼叫物件的位置引數元組。
kwargs表示呼叫物件的字典。
name為程式的別名。
group實質上不使用,為None。
方法:is_alive()、join([timeout])、run()、start()、terminate()。其中,Process以start()啟動某個程式,並自動呼叫run方法.
屬性:authkey、daemon(要透過start()設定,必須設定在方法start之前)、exitcode(程式在執行時為None、如果為–N,表示被訊號N結束)、name、pid。其中daemon是父程式終止後自動終止,且自己不能產生新程式,必須在start()之前設定。
2.3 建立單程式
單執行緒比較簡單,建立一個 Process的例項物件就好,傳入引數 target 為已經定義好的方法worker以及worker需要的引數
-
#!/usr/bin/env python
-
# encoding: utf-8
-
"""
-
author: yangyi@youzan.com
-
time: 2017/7/2 下午6:45
-
func:
-
"""
-
import multiprocessing
-
import datetime, time
-
def worker(interval):
-
print("process start: {0}".format(datetime.datetime.today()));
-
time.sleep(interval)
-
print("process end: {0}".format(datetime.datetime.today()));
-
-
if __name__ == "__main__":
-
p = multiprocessing.Process(target=worker, args=(5,))
-
p.start()
-
p.join()
- print "end!"
-
#!/usr/bin/env python
-
# encoding: utf-8
-
"""
-
author: yangyi@youzan.com
-
time: 2017/7/2 下午7:50
-
func:
-
"""
-
import multiprocessing
-
def worker(num):
-
print "worker %d" %num
-
-
-
if __name__ == "__main__":
-
print("The number of CPU is:" + str(multiprocessing.cpu_count()))
-
proc = []
-
for i in xrange(5):
-
p = multiprocessing.Process(target=worker, args=(i,))
-
proc.append(p)
-
for p in proc:
-
p.start()
-
for p in proc:
-
p.join()
- print "end ..."
點選(此處)摺疊或開啟
-
The number of CPU is:4
-
worker 0
-
worker 1
-
worker 2
-
worker 3
-
worker 4
- main process end ...
multiprocessing提供程式池的類--Pool,它可以指定程式最大可以呼叫的程式數量,當有新的請求提交到pool中時,如果程式池還沒有滿,那麼就會建立一個新的程式用來執行該請求;但如果程式池中的程式數已經達到規定最大值,那麼該請求就會等待,直到池中有程式結束,才會建立新的程式來它。
構造方法:
Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])
processes : 使用的工作程式的數量,如果processes是None,預設使用os.cpu_count()返回的數量。
initializer: 如果initializer是None,那麼每一個工作程式在開始的時候會呼叫initializer(*initargs)。
maxtasksperchild:工作程式退出之前可以完成的任務數,完成後用一個新的工作程式來替代原程式,來讓閒置的資源被釋放。maxtasksperchild預設是None,意味著只要Pool存在工作程式就會一直存活。
context: 用在制定工作程式啟動時的上下文,一般使用multiprocessing.Pool()或者一個context物件的Pool()方法來建立一個池,兩種方法都適當的設定了context。
例項方法:
apply(func[, args[, kwds]]):同步程式池
apply_async(func[, args[, kwds[, callback[, error_callback]]]]) :非同步程式池
close() : 關閉程式池,阻止更多的任務提交到pool,待任務完成後,工作程式會退出。
terminate() : 結束工作程式,不在處理未完成的任務.
join() : 等待工作執行緒的退出,在呼叫join()前必須呼叫close()或者 terminate(),因為被終止的程式需要被父程式呼叫wait(join等價與wait),否則程式會成為殭屍程式。
-
#!/usr/bin/env python
-
# encoding: utf-8
-
"""
-
author: yangyi@youzan.com
-
time: 2017/7/2 下午7:50
-
func:
-
"""
-
from multiprocessing import Pool
-
import time
-
def worker(num):
-
print "worker %d" %num
-
time.sleep(2)
-
print "end worker %d" %num
-
-
if __name__ == "__main__":
-
proc_pool = Pool(2)
-
for i in xrange(4):
-
proc_pool.apply_async(worker, (i,)) #使用了非同步呼叫,從輸出結果可以看出來
-
-
proc_pool.close()
-
proc_pool.join()
- print "main process end ..."
點選(此處)摺疊或開啟
-
worker 0
-
worker 1
-
end worker 0
-
end worker 1
-
worker 2
-
worker 3
-
end worker 2
-
end worker 3
- main process end ..
2.6 使用queue
multiprocessing提供佇列類,可以透過呼叫multiprocessing.Queue(maxsize) 初始化佇列物件,maxsize表示佇列裡面最多的元素個數。
例子 建立了兩個函式入隊,出隊,出隊處理時使用了lock特性,序列化取資料。
-
#!/usr/bin/env python
-
# encoding: utf-8
-
"""
-
author: yangyi@youzan.com
-
time: 2017/7/2 下午9:03
-
func:
-
"""
-
import time
-
from multiprocessing import Process, current_process,Lock,Queue
-
import datetime
-
def inputQ(queue):
-
time.sleep(1)
-
info = "proc_name: " + current_process().name + ' was putted in queue at: ' + str(datetime.datetime.today())
-
queue.put(info)
-
def outputQ(queue,lock):
-
info = queue.get()
-
lock.acquire()
-
print ("proc_name: " + current_process().name + ' gets info :' + info)
-
lock.release()
-
if __name__ == '__main__':
-
record1 = [] # store input processes
-
record2 = [] # store output processes
-
lock = Lock() # To prevent messy print
-
queue = Queue(3)
-
for i in range(10):
-
process = Process(target=inputQ, args=(queue,))
-
process.start()
-
record1.append(process)
-
for i in range(10):
-
process = Process(target=outputQ, args=(queue,lock))
-
process.start()
-
record2.append(process)
-
for p in record1:
-
p.join()
-
queue.close() # No more object will come, close the queue
-
for p in record2:
- p.join()
Pipe可以是單向(half-duplex),也可以是雙向(duplex)。我們透過mutiprocessing.Pipe(duplex=False)建立單向管道 (預設為雙向)。一個程式從PIPE一端輸入物件,然後被PIPE另一端的程式接收,單向管道只允許管道一端的程式輸入,而雙向管道則允許從兩端輸入。
用法 multiprocessing.Pipe([duplex])
該類返回一組物件例項(conn1, conn2),分別代表傳送和接受訊息的兩端。
-
#!/usr/bin/env python
-
# encoding: utf-8
-
"""
-
author: yangyi@youzan.com
-
time: 2017/7/2 下午8:01
-
func:
-
"""
-
from multiprocessing import Process, Pipe
-
def p1(conn, name):
-
conn.send('hello ,{name}'.format(name=name))
-
print "p1 receive :", conn.recv()
-
conn.close()
-
-
def p2(conn, name):
-
conn.send('hello ,{name}'.format(name=name))
-
print "p2 receive :", conn.recv()
-
conn.close()
-
-
if __name__ == '__main__':
-
parent_conn, child_conn = Pipe()
-
proc1 = Process(target=p1, args=(child_conn, "parent_conn"))
-
proc2 = Process(target=p2, args=(parent_conn, "child_conn"))
-
proc1.start()
-
proc2.start()
-
proc1.join()
- proc2.join()
點選(此處)摺疊或開啟
-
p1 receive : hello ,child_conn
- p2 receive : hello ,parent_conn
2.8 daemon程式對比結果
-
import multiprocessing
-
import datetime, time
-
def worker(interval):
-
print("process start: {0}".format(datetime.datetime.today()));
-
time.sleep(interval)
-
print("process end: {0}".format(datetime.datetime.today()));
-
if __name__ == "__main__":
-
p = multiprocessing.Process(target=worker, args=(5,))
-
p.start()
- print "end!"
點選(此處)摺疊或開啟
- end!
- process start: 2017-07-02 18:47:30.656244
- process end: 2017-07-02 18:47:35.657464
設定 daemon = True,程式隨著主程式結束而不等待子程式。
-
import multiprocessing
-
import datetime, time
-
def worker(interval):
-
print("process start: {0}".format(datetime.datetime.today()));
-
time.sleep(interval)
-
print("process end: {0}".format(datetime.datetime.today()));
-
if __name__ == "__main__":
-
p = multiprocessing.Process(target=worker, args=(5,))
-
p.daemon = True
-
p.start()
- print "end!"
end!
因為子程式設定了daemon屬性,主程式結束,multiprocessing建立的程式物件就隨著結束了。
-
import multiprocessing
-
import datetime, time
-
def worker(interval):
-
print("process start: {0}".format(datetime.datetime.today()));
-
time.sleep(interval)
-
print("process end: {0}".format(datetime.datetime.today()));
-
if __name__ == "__main__":
-
p = multiprocessing.Process(target=worker, args=(5,))
-
p.daemon = True #
-
p.start()
-
p.join() #程式執行完畢後再關閉
- print "end!"
點選(此處)摺疊或開啟
-
process start: 2017-07-02 18:48:20.953754
-
process end: 2017-07-02 18:48:25.954736
-
當多個程式需要訪問共享資源的時候,Lock可以用來避免訪問的衝突。
例項方法:
acquire([timeout]): 使執行緒進入同步阻塞狀態,嘗試獲得鎖定。
release(): 釋放鎖。使用前執行緒必須已獲得鎖定,否則將丟擲異常。
例子:
多個程式使用同一個std_out ,使用lock機制確保同一個時刻有一個一個程式獲取輸出。
-
#!/usr/bin/env python
# encoding: utf-8
"""
author: yangyi@youzan.com
time: 2017/7/2 下午9:28
func:
"""
from multiprocessing import Process, Lock
def func_with_lock(l, i):
l.acquire()
print 'hello world', i
l.release()
def func_without_lock(i):
print 'hello world', i
if __name__ == '__main__':
lock = Lock()
print "func_with_lock :"
for num in range(10):
Process(target=func_with_lock, args=(lock, num)).start()
點選(此處)摺疊或開啟
-
func_with_lock :
-
hello world 0
-
hello world 1
-
hello world 2
-
hello world 3
-
hello world 4
-
hello world 5
-
hello world 6
-
hello world 7
-
hello world 8
- hello world 9
三 小結
本文參考官方資料以及其他資源,對multiprocesssing 的使用方式做了總結,還有很多知識需要詳細閱讀官方文件。紙上來得終覺淺,絕知此事要躬行。參考資料
[1]
[2]Python標準庫10 多程式初步 (multiprocessing包)
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22664653/viewspace-2141502/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Python集合淺談Python
- 淺談Python基礎Python
- 【Python】淺談裝飾器Python
- 淺談Python裝飾器Python
- 淺談 Python 的 with 語句Python
- 【Python】淺談python中的jsonPythonJSON
- 【Multiprocessing系列】Multiprocessing基礎
- 淺談python中的xpath用法Python
- python中socket+multiprocessing多程式Python
- Python分享之多程式探索 (multiprocessing包)Python
- Python多程式之分享(multiprocessing包)Python
- Python分享之多程式初步 (multiprocessing包)Python
- 淺談Python專案開發&管理Python
- 淺談Python中的私有變數Python變數
- python淺談正則的常用方法Python
- 淺談python模組的匯入操作Python
- 淺談五大Python Web框架PythonWeb框架
- 淺淺談ReduxRedux
- Python程式專題2:multiprocessing建立程式Python
- [轉載] Python程式——multiprocessing.Event()|Barrier()Python
- python中的Queue與多程式(multiprocessing)Python
- Python3之淺談----深拷貝與淺拷貝Python
- PHPer 淺談 Python 的資料結構PHPPython資料結構
- 淺談設計模式及python實現設計模式Python
- Python __getattribute__ vs __getattr__ 淺談Python
- 由 sort 中 key 的用法淺談 pythonPython
- Python併發程式設計系列之多程式(multiprocessing)Python程式設計
- Python 多程式 multiprocessing.Pool類詳解Python
- 淺淺淺談JavaScript作用域JavaScript
- Web | 淺談用Python進行Web開發WebPython
- 淺談Python中的bs4基礎Python
- 淺談我對python中的monkey patchPython
- Celery淺談
- 淺談flutterFlutter
- 淺談JMM
- 淺談反射反射
- 淺談mockMock
- 淺談SYNPROXY