Python多進程使用與總結
1.概要
? 眾所周知,由于GIL鎖的存在,Python多線程并不是真正意義上的多線程,不能很好的利用多核CPU,為了充分的利用系統資源,py提供了multiprocessing多進程庫,其支持子進程、通信和數據共享、執行不同形式的同步,提供了Process、Queue、Pipe、Lock等組件。[參考](https://www.cnblogs.com/tkqasn/p/5701230.html)
- 在UNIX平臺上,當某個進程終結之后,該進程需要被其父進程調用wait,否則進程成為僵尸進程(Zombie)。所以,有必要對每個Process對象調用join()方法 (實際上等同于wait)。對于多線程來說,由于只有一個進程,所以不存在此必要性。
- multiprocessing提供了threading包中沒有的IPC(比如Pipe和Queue),效率上更高。應優先考慮Pipe和Queue,避免使用Lock/Event/Semaphore/Condition等同步方式 (因為它們占據的不是用戶進程的資源)。
- 多進程應該避免共享資源。在多線程中,我們可以比較容易地共享資源,比如使用全局變量或者傳遞參數。在多進程情況下,由于每個進程有自己獨立的內存空間,以上方法并不合適。此時我們可以通過共享內存和Manager的方法來共享資源。但這樣做提高了程序的復雜度,并因為同步的需要而降低了程序的效率。
- Process.PID中保存有PID,如果進程還沒有start(),則PID為None。
注意:window下啟動子進程必須在if name == "main"語句后面寫相關語句,這個坑踩過
注意:window下啟動子進程必須在if name == "main"語句后面寫相關語句,這個坑踩過
2.使用實例
2.1 創建多進程
方式一:直接使用Process
import multiprocessing
import queue
import time
import random
import os
def out(msg):
s = random.randint(1, 3) #隨機延時
time.sleep(s)
print("msg:{} time:{} pid:{} ppid:{}".format(msg, time.asctime(time.localtime(time.time())), os.getpid(), os.getppid()))
def que():
Queue = queue.Queue()#創建隊列 先進先出
for q in range(10):
Queue.put(q)
return Queue
if __name__ == "__main__":
Que = que()
for i in range(10):
p = multiprocessing.Process(target=out, args=(Que.get(), ))
p.start()
# p.join() # location1
# p.join() #location2
print("我是主進程")
基本邏輯:創建一個動作函數out() ,增加隨機時間的延時,以便查看控制臺輸出差異,輸出msg、當前時間、子進程pid、父進程pid。定義一個隊列函數que(),作為生產者,為后面取得輸出參數做好準備。p:創建一個進程,p.start()開啟一個進程,p.join()阻塞主進程,直到子進程執行完畢.注意:window下啟動子進程必須在if name == "main"語句后面寫相關語句,這個坑踩過(不知道為什么這個markdown編輯器,雙下滑線沒有顯示,可能是渲染引擎問題),args參數是元組類型,若是只有一個參數,需要加上最后
,
-
location1處和location2處join均注釋,主進程在子進程之前執行完畢,控制臺輸出:
我是主進程 msg:2 time:Sun Mar 31 20:45:22 2019 pid:17068 ppid:28044 msg:4 time:Sun Mar 31 20:45:22 2019 pid:25004 ppid:28044 msg:5 time:Sun Mar 31 20:45:22 2019 pid:14576 ppid:28044 msg:7 time:Sun Mar 31 20:45:23 2019 pid:26384 ppid:28044 msg:9 time:Sun Mar 31 20:45:23 2019 pid:3368 ppid:28044 msg:1 time:Sun Mar 31 20:45:23 2019 pid:25052 ppid:28044 msg:3 time:Sun Mar 31 20:45:23 2019 pid:23948 ppid:28044 msg:0 time:Sun Mar 31 20:45:24 2019 pid:13660 ppid:28044 msg:6 time:Sun Mar 31 20:45:24 2019 pid:1052 ppid:28044 msg:8 time:Sun Mar 31 20:45:25 2019 pid:13968 ppid:28044
-
只有location2處join注釋,主進程在子進程之后執行,但是可看出輸出順序是0~9,總的時間特別長,阻塞輸出,控制臺輸出
msg:0 time:Sun Mar 31 20:51:06 2019 pid:11048 ppid:3936 msg:1 time:Sun Mar 31 20:51:09 2019 pid:18504 ppid:3936 msg:2 time:Sun Mar 31 20:51:10 2019 pid:15580 ppid:3936 msg:3 time:Sun Mar 31 20:51:12 2019 pid:25180 ppid:3936 msg:4 time:Sun Mar 31 20:51:15 2019 pid:25912 ppid:3936 msg:5 time:Sun Mar 31 20:51:17 2019 pid:22660 ppid:3936 msg:6 time:Sun Mar 31 20:51:21 2019 pid:13224 ppid:3936 msg:7 time:Sun Mar 31 20:51:23 2019 pid:14204 ppid:3936 msg:8 time:Sun Mar 31 20:51:24 2019 pid:2940 ppid:3936 msg:9 time:Sun Mar 31 20:51:28 2019 pid:9068 ppid:3936 我是主進程
-
只有location1處join注釋,主進程在子進程之后執行,其中很多進程執行時間相同,而且順序錯亂,總時間短,控制臺輸出
msg:1 time:Sun Mar 31 20:52:04 2019 pid:24448 ppid:13532 msg:5 time:Sun Mar 31 20:52:04 2019 pid:23376 ppid:13532 msg:2 time:Sun Mar 31 20:52:05 2019 pid:22048 ppid:13532 msg:6 time:Sun Mar 31 20:52:05 2019 pid:18968 ppid:13532 msg:7 time:Sun Mar 31 20:52:05 2019 pid:26208 ppid:13532 msg:0 time:Sun Mar 31 20:52:06 2019 pid:15948 ppid:13532 msg:3 time:Sun Mar 31 20:52:06 2019 pid:26724 ppid:13532 msg:4 time:Sun Mar 31 20:52:06 2019 pid:13220 ppid:13532 msg:8 time:Sun Mar 31 20:52:06 2019 pid:24384 ppid:13532 msg:9 time:Sun Mar 31 20:52:06 2019 pid:4392 ppid:13532 我是主進程
方式二:重寫run()方法
from multiprocessing import Process
import queue
import time
import random
import os
class Myprocess(Process):
def __init__(self, msg):
super(Myprocess, self).__init__()# 或者Process.__init__(self)
self.msg = msg
def run(self):
s = random.randint(1, 3) # 隨機延時
time.sleep(s)
print("msg:{} time:{} pid:{} ppid:{}".format(self.msg, time.asctime(time.localtime(time.time())), os.getpid(),
os.getppid()))
def que():
Queue = queue.Queue() # 創建隊列 先進先出
for q in range(10):
Queue.put(q)
return Queue
if __name__ == "__main__":
Que = que()
for i in range(10):
p = Myprocess(msg=Que.get())
p.start()
p.join()
print("我是主進程")
基本邏輯:定義一個子類,繼承Process類,因為不想完全重寫父類的init,所以這里使用super()或者Process.init(self)將子類參數傳遞給父類。其中run()方法為動作函數,這個也是來自父類的方法,這里因為準備完全重寫父類的方法,所以不用super()方法,也可以使用super(Myprocess, self).run()來避免完全重寫父類方法。函數功能run()對應前面的out()函數功能。
super() 函數是用于調用父類(超類)的一個方法。super 是用來解決多重繼承問題的,直接用類名調用父類方法在使用單繼承的時候沒問題,但是如果使用多繼承,會涉及到查找順序(MRO)、重復調用(鉆石繼承)等種種問題。MRO 就是類的方法解析順序表, 其實也就是繼承父類方法時的順序表
-
沒弄明白為什么這個主進程沒有阻塞,控制臺輸出:
msg:2 time:Sun Mar 31 21:40:37 2019 pid:4396 ppid:23176 msg:3 time:Sun Mar 31 21:40:37 2019 pid:18640 ppid:23176 msg:8 time:Sun Mar 31 21:40:37 2019 pid:13612 ppid:23176 msg:9 time:Sun Mar 31 21:40:37 2019 pid:13196 ppid:23176 我是主進程 msg:1 time:Sun Mar 31 21:40:38 2019 pid:16324 ppid:23176 msg:4 time:Sun Mar 31 21:40:38 2019 pid:14560 ppid:23176 msg:5 time:Sun Mar 31 21:40:38 2019 pid:6376 ppid:23176 msg:6 time:Sun Mar 31 21:40:38 2019 pid:26088 ppid:23176 msg:7 time:Sun Mar 31 21:40:38 2019 pid:23056 ppid:23176 msg:0 time:Sun Mar 31 21:40:39 2019 pid:14724 ppid:23176
2.2 Process類
構造方法:
Process([group [, target [, name [, args [, kwargs]]]]])
group: 線程組,目前還沒有實現,庫引用中提示必須是None;
target: 要執行的方法;
name: 進程名;
args/kwargs: 要傳入方法的參數。。實例方法:
is_alive():返回進程是否在運行。
join([timeout]):阻塞當前上下文環境的進程程,直到調用此方法的進程終止或到達指定的timeout(可選參數)。
start():進程準備就緒,等待CPU調度
run():strat()調用run方法,如果實例進程時未制定傳入target,這star執行t默認run()方法。
terminate():不管任務是否完成,立即停止工作進程,但是子進程如果存在沒完成,就會變成僵尸進程
屬性:
authkey
daemon:和線程的setDeamon功能一樣,守護進程,父進程死了子進程也死了
exitcode(進程在運行時為None、如果為–N,表示被信號N結束)
name:進程名字。
pid:進程號。
- is_alive()、p.name、p.pid 、p.exitcode:實例:下面代碼用的是重寫的Myprocess,直接使用Process也是一樣的
..........
p = Myprocess(msg=10086)
p.start()
print("is_alive() :{}".format(p.is_alive()))
print("p.exitcode: {}".format(p.exitcode))
p.join()
print("is_alive() :{}".format(p.is_alive()))
print("p.exitcode: {}".format(p.exitcode))
print("p.name :{}".format(p.name))
print("p.pid :{}".format(p.pid))
print("我是主進程")
-
控制臺輸出:
is_alive() :True p.exitcode: None msg:10086 time:Sun Mar 31 22:04:38 2019 pid:22796 ppid:10980 is_alive() :False p.exitcode: 0 p.name :Myprocess-1 p.pid :22796 我是主進程
setDaemon()方法。主進程A中,創建了子進程B,并且在主進程A中調用了B.setDaemon(),這個的意思是,把主進程A設置為守護進程,這時候,要是主進程A執行結束了,就不管子進程B是否完成,一并和進程程A退出.這就是setDaemon方法的含義,這基本和join是相反的。此外,還有個要特別注意的:必須在start() 方法調用之前設置,默認False
代碼,例1:
.......
p = Myprocess(msg=10086)
p.daemon = True
p.start()
# p.join()
print("我是主進程")
-
輸出1:
我是主進程 Process finished with exit code 0
-
代碼,例2:
........ p = Myprocess(msg=10086) # p.daemon = True p.start() # p.join() print("我是主進程")
-
輸出2:
我是主進程 msg:10086 time:Sun Mar 31 22:21:34 2019 pid:6788 ppid:556
2.3 Pool類
進程池內部維護一個進程序列,當使用時,則去進程池中獲取一個進程,如果進程池序列中沒有可供使用的進進程,那么程序就會等待,直到進程池中有可用進程為止。進程池設置最好等于CPU核心數量
構造方法:
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() : wait工作線程的退出,在調用join()前,必須調用close() or terminate()。這樣是因為被終止的進程需要被父進程調用wait(join等價與wait),否則進程會成為僵尸進程。pool.join()必須使用在
-
進程池異步:
import multiprocessing import queue import time import random import os def out(msg): s = random.randint(1, 3) #隨機延時 time.sleep(s) return "msg:{} time:{} pid:{} ppid:{}".format(msg, time.asctime(time.localtime(time.time())), os.getpid(), os.getppid()) # 接收函數 def callback(arg): print(arg) def que(): Queue = queue.Queue()#創建隊列 先進先出 for q in range(10): Queue.put(q) return Queue if __name__ == "__main__": Que = que() pool = multiprocessing.Pool(processes=multiprocessing.cpu_count())#取最大CPU數 for i in range(10): pool.apply_async(func=out, args=(Que.get(),), callback=callback) #callback自動接收func返回來的內容 pool.close() pool.join() # 進程池中進程執行完畢后再關閉,如果注釋,那么程序直接關閉 print("我是主進程")
-
控制臺輸出:總時間短,且執行順序亂,非阻塞
msg:2 time:Sun Mar 31 22:35:44 2019 pid:23020 ppid:16652 msg:3 time:Sun Mar 31 22:35:44 2019 pid:26476 ppid:16652 msg:6 time:Sun Mar 31 22:35:44 2019 pid:26204 ppid:16652 msg:7 time:Sun Mar 31 22:35:44 2019 pid:17256 ppid:16652 msg:0 time:Sun Mar 31 22:35:45 2019 pid:26980 ppid:16652 msg:4 time:Sun Mar 31 22:35:45 2019 pid:10248 ppid:16652 msg:1 time:Sun Mar 31 22:35:46 2019 pid:23180 ppid:16652 msg:5 time:Sun Mar 31 22:35:46 2019 pid:22796 ppid:16652 msg:8 time:Sun Mar 31 22:35:47 2019 pid:23020 ppid:16652 msg:9 time:Sun Mar 31 22:35:47 2019 pid:26476 ppid:16652 我是主進程
-
進程池同步:
import multiprocessing import queue import time import random import os def out(msg): s = random.randint(1, 3) #隨機延時 time.sleep(s) print("msg:{} time:{} pid:{} ppid:{}".format(msg, time.asctime(time.localtime(time.time())), os.getpid(), os.getppid())) def que(): Queue = queue.Queue()#創建隊列 先進先出 for q in range(10): Queue.put(q) return Queue if __name__ == "__main__": Que = que() pool = multiprocessing.Pool(processes=multiprocessing.cpu_count()) for i in range(10): pool.apply(func=out, args=(Que.get(),)) #callback自動接收func返回來的內容 pool.close() pool.join() # 進程池中進程執行完畢后再關閉,如果注釋,那么程序直接關閉 print("我是主進程")
-
控制臺輸出:總時間長,順序執行,阻塞
msg:0 time:Sun Mar 31 22:40:36 2019 pid:24072 ppid:13240 msg:1 time:Sun Mar 31 22:40:37 2019 pid:13552 ppid:13240 msg:2 time:Sun Mar 31 22:40:40 2019 pid:13736 ppid:13240 msg:3 time:Sun Mar 31 22:40:43 2019 pid:8040 ppid:13240 msg:4 time:Sun Mar 31 22:40:46 2019 pid:12532 ppid:13240 msg:5 time:Sun Mar 31 22:40:48 2019 pid:24272 ppid:13240 msg:6 time:Sun Mar 31 22:40:49 2019 pid:14028 ppid:13240 msg:7 time:Sun Mar 31 22:40:52 2019 pid:22340 ppid:13240 msg:8 time:Sun Mar 31 22:40:55 2019 pid:24072 ppid:13240 msg:9 time:Sun Mar 31 22:40:56 2019 pid:13552 ppid:13240 我是主進程
如果動作函數是帶返回值的不僅可以使用callback輸出還可以使用get()方法
-
錯誤的get使用方法:
def out(msg): s = random.randint(1, 3) #隨機延時 time.sleep(s) print("msg:{} time:{} pid:{} ppid:{}".format(msg, time.asctime(time.localtime(time.time())), os.getpid(), os.getppid())) return msg ... ..... .........省略........ if __name__ == "__main__": Que = que() pool = multiprocessing.Pool(processes=multiprocessing.cpu_count()) l = [] for i in range(10): r = pool.apply_async(func=out, args=(Que.get(),)) print(r.get()) # l.append(r) pool.close() pool.join() # 進程池中進程執行完畢后再關閉,如果注釋,那么程序直接關閉 # for i in l: # print(i.get()) print("我是主進程")
-
控制臺輸出:順訊執行,時間長,阻塞
msg:0 time:Sun Mar 31 22:49:22 2019 pid:15668 ppid:13468 0 msg:1 time:Sun Mar 31 22:49:24 2019 pid:5968 ppid:13468 1 msg:2 time:Sun Mar 31 22:49:25 2019 pid:25524 ppid:13468 2 msg:3 time:Sun Mar 31 22:49:28 2019 pid:14148 ppid:13468 3 msg:4 time:Sun Mar 31 22:49:31 2019 pid:15240 ppid:13468 4 msg:5 time:Sun Mar 31 22:49:32 2019 pid:22544 ppid:13468 5 msg:6 time:Sun Mar 31 22:49:35 2019 pid:5364 ppid:13468 6 msg:7 time:Sun Mar 31 22:49:37 2019 pid:24740 ppid:13468 7 msg:8 time:Sun Mar 31 22:49:40 2019 pid:15668 ppid:13468 8 msg:9 time:Sun Mar 31 22:49:43 2019 pid:5968 ppid:13468 9 我是主進程
-
錯誤的get使用方法:
........................... Que = que() pool = multiprocessing.Pool(processes=multiprocessing.cpu_count()) l = [] for i in range(10): r = pool.apply_async(func=out, args=(Que.get(),)) #callback自動接收func返回來的內容 # print(r.get()) l.append(r) pool.close() pool.join() # 進程池中進程執行完畢后再關閉,如果注釋,那么程序直接關閉 for i in l: print(i.get()) print("我是主進程")
-
控制臺輸出:順序錯亂,總時間短,非阻塞
msg:6 time:Sun Mar 31 22:53:28 2019 pid:8600 ppid:25532 msg:1 time:Sun Mar 31 22:53:29 2019 pid:5960 ppid:25532 msg:0 time:Sun Mar 31 22:53:29 2019 pid:24544 ppid:25532 msg:3 time:Sun Mar 31 22:53:29 2019 pid:15672 ppid:25532 msg:4 time:Sun Mar 31 22:53:29 2019 pid:21808 ppid:25532 msg:8 time:Sun Mar 31 22:53:29 2019 pid:8600 ppid:25532 msg:7 time:Sun Mar 31 22:53:29 2019 pid:19028 ppid:25532 msg:2 time:Sun Mar 31 22:53:30 2019 pid:9820 ppid:25532 msg:5 time:Sun Mar 31 22:53:30 2019 pid:12736 ppid:25532 msg:9 time:Sun Mar 31 22:53:32 2019 pid:24544 ppid:25532 0 1 2 3 4 5 6 7 8 9 我是主進程
2.4 數據共享
進程各自持有一份數據,默認無法共享數據
-
Array()實現:
- C語言參數類型對照
‘c’: ctypes.c_char ‘u’: ctypes.c_wchar ‘b’: ctypes.c_byte ‘B’: ctypes.c_ubyte ‘h’: ctypes.c_short ‘H’: ctypes.c_ushort ‘i’: ctypes.c_int ‘I’: ctypes.c_uint ‘l’: ctypes.c_long, ‘L’: ctypes.c_ulong ‘f’: ctypes.c_float ‘d’: ctypes.c_double
-
代碼塊和輸出:
from multiprocessing import Process, Array def f(a): a[0] = a[-1] if __name__ == '__main__': arr = Array('u', "Hello World") p = Process(target=f, args=(arr,)) p.start() p.join() print(arr[:]) -----控制臺輸出------ dello World Process finished with exit code 0
-
Manager()實現:
Manager()返回的manager提供list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and 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__': manager = Manager() d = manager.dict() l = manager.list(range(10)) p = Process(target=f, args=(d, l)) p.start() p.join() print(d) print(l)
-
控制臺輸出:
{1: '1', '2': 2, 0.25: None} [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] Process finished with exit code 0
2.5 Lock互斥鎖
當多個進程需要訪問共享資源的時候,Lock可以用來避免訪問的沖突
進程之間數據隔離,但是共享一套文件系統,因而可以通過文件來實現進程直接的通信,但問題是必須自己加鎖處理。
注意:加鎖的目的是為了保證多個進程修改同一塊數據時,同一時間只能有一個修改,即串行的修改,沒錯,速度是慢了,犧牲了速度而保證了數據安全
-
代碼實例,不加鎖:
from multiprocessing import Process, Lock import time import random def write(lock, i): # lock.acquire() file(i) read() # lock.release() def file(a): with open("test.txt", "w") as f: f.write(a) def read(): with open("test.txt", "r") as f: line = f.readline() print("a: {} ".format(line)) if __name__ == "__main__": lock = Lock() file(str(0)) read() for i in range(10): p = Process(target=write, args=(lock, str(i))) p.start() p.join() print("我是主進程")
-
控制臺輸出:可見有幾次讀到的都是0,表明某個時刻幾個進程都同時去訪問了該文件
a: 0 a: 2 a: 0 a: 0 a: 3 a: 5 a: 4 a: 6 a: 7 a: 8 a: 9 我是主進程
-
代碼實例,加鎖:
.......... def write(lock, i): lock.acquire() file(i) read() lock.release() ...........
-
控制臺輸出:
a: 0 a: 1 a: 0 a: 2 a: 3 a: 4 a: 6 a: 5 a: 7 a: 8 a: 9 我是主進程
2.6 Semaphore
用來控制對共享資源的訪問數量
-
普通代碼示例
import multiprocessing import time def worker(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(10): p = multiprocessing.Process(target = worker, args=(i*2, )) p.start()
-
控制臺輸出:觀察換行,可以看出同一時段,會有很多進程都在訪問這個worke函數
Process-1acquire Process-2acquire Process-1release Process-3acquire Process-4acquire Process-5acquire Process-7acquire Process-6acquire Process-8acquire Process-9acquire Process-10acquire Process-2release Process-3release Process-4release Process-5release Process-6release Process-7release Process-8release Process-9release Process-10release Process finished with exit code 0
-
用Semaphore代碼實例:
# -*- coding: utf-8 -*- # @Time : 2019/3/31 10:42 # @Author : YuChou # @Site : # @File : examp.py # @Software: PyCharm import multiprocessing import time def worker(i,s): 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(10): p = multiprocessing.Process(target = worker, args=(i*2, s)) p.start()
-
控制臺輸出:觀察換行,可以看出同時并存最多只有兩個進程
Process-3acquire Process-1acquire Process-1release Process-4acquire Process-3release Process-2acquire Process-2release Process-5acquire Process-4release Process-6acquire Process-5release Process-7acquire Process-6release Process-8acquire Process-7release Process-9acquire Process-8release Process-10acquire Process-9release Process-10release
2.7 Event
用于進程間通信,即程序中的其一個線程需要通過判斷某個進程的狀態來確定自己下一步的操作,就用到了event對象
event對象默認為假(Flase),即遇到event對象在等待就阻塞線程的執行
可以通過is_set()查看當前狀態
event.wait() #括號里可以帶數字執行,數字表示等待的秒數,不帶數字表示一直阻塞狀態
event.set() #默認為False,set一次表示True,所以子線程里的foo函數解除阻塞狀態繼續執行
-
主進程和子進程之間的通信:
import multiprocessing import time def child(even): print("start child.....") even.wait() print("end child.....") if __name__ == "__main__": even = multiprocessing.Event() p = multiprocessing.Process(target=child, args=(even,)) p.start() print("主進程等待....") time.sleep(4) print("主進程結束等待") even.set()
-
控制臺輸出:可以看出,子進程一直在等set
主進程等待.... start child..... 主進程結束等待 end child.....
-
給與wait值為2s
def child(even, s): print("start child.....") even.wait(s) print("end child.....") if __name__ == "__main__": even = multiprocessing.Event() p = multiprocessing.Process(target=child, args=(even,2)) p.start() print("主進程等待....") time.sleep(4) print("主進程結束等待") even.set()
-
控制臺輸出:可見并沒有等待set, 子進程已經解除阻塞了
主進程等待.... start child..... end child..... 主進程結束等待
- 當然也可以用在子進程與子進程之間,不詳解
2.8 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的一段示例代碼:
-
代碼塊:來源https://www.cnblogs.com/kaituorensheng/p/4445418.html
import multiprocessing def writer_proc(q): try: q.put(1, block = False) except: pass def reader_proc(q): try: print(q.get(block = False)) except: pass if __name__ == "__main__": q = multiprocessing.Queue() writer = multiprocessing.Process(target=writer_proc, args=(q,)) writer.start() reader = multiprocessing.Process(target=reader_proc, args=(q,)) reader.start() reader.join() writer.join()
-
控制臺輸出
1
2.9 Pipe
Pipe方法返回(conn1, conn2)代表一個管道的兩個端。Pipe方法有duplex參數,如果duplex參數為True(默認值),那么這個管道是全雙工模式,也就是說conn1和conn2均可收發。duplex為False,conn1只負責接受消息,conn2只負責發送消息。
send和recv方法分別是發送和接受消息的方法。例如,在全雙工模式下,可以調用conn1.send發送消息,conn1.recv接收消息。如果沒有消息可接收,recv方法會一直阻塞。如果管道已經被關閉,那么recv方法會拋出EOFError。close方法表示關閉管道,當消息接收結束以后,關閉管道。
-
代碼:
import multiprocessing import time def pro(p): for i in range(10): p.send(i) print("我發啦") time.sleep(1) def cou(c): n=0 while n <= 9: print(c.recv()) print("我收啦\n") time.sleep(1) n += 1 if __name__ == "__main__": p, c = multiprocessing.Pipe() m1 = multiprocessing.Process(target=pro, args=(p,)) m2 = multiprocessing.Process(target=cou, args=(c,)) m1.start() m2.start() m1.join() m2.join()
-
控制臺輸出
我發啦 0 我收啦 我發啦 1 我收啦 我發啦 2 我收啦 我發啦 3 我收啦 我發啦 4 我收啦 我發啦 5 我收啦 我發啦 6 我收啦 我發啦 7 我收啦 我發啦 8 我收啦 我發啦 9 我收啦
2.10 subprocess三方模塊
很多時候,子進程并不是自身,而是一個外部進程。我們創建了子進程后,還需要控制子進程的輸入和輸出。
subprocess
模塊可以讓我們非常方便地啟動一個子進程,然后控制其輸入和輸出。
-
代碼塊:
import subprocess r = subprocess.call(["python", "--version"]) #命令行空格用 ','隔開 print('console:', r)
-
控制臺輸出:
Python 3.6.5 :: Anaconda, Inc. console: 0
參考鏈接:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431927781401bb47ccf187b24c3b955157bb12c5882d000
https://www.cnblogs.com/kaituorensheng/p/4445418.html
https://www.cnblogs.com/UncleYong/p/6987112.html
https://www.cnblogs.com/haiyan123/p/7429568.html
https://www.cnblogs.com/lidagen/p/7252247.html
https://blog.51cto.com/286577399/2051155
2019/04/01 22:26
補充:多進程的map使用:
import multiprocessing
import requests
import time
import random
def get(_):
time.sleep(random.randint(0, 5))
url = "http://www.baidu.com"
r = requests.get(url)
print(r.status_code)
if __name__ == "__main__":
pool = multiprocessing.Pool(multiprocessing.cpu_count())
pool.map(get, range(20))
2019/04/10 22:58