Python多進程

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

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容