1. 程式人生 > >多進程學習總結

多進程學習總結

通信 don lba queue cal problem 發揮 發送數據 info

Python 解釋器有一個全局解釋器鎖(PIL),導致每個 Python 進程中最多同時運行一個線程,因此 Python 多線程程序並不能改善程序性能,不能發揮多核系統的優勢,可以通過這篇文章了解。但是多進程程序不受此影響, Python 2.6 引入了 multiprocessing 來解決這個問題。這裏介紹 multiprocessing 模塊下的進程,進程同步,進程間通信和進程管理四個方面的內容。 這裏主要講解多進程的典型使用,multiprocessing 的 API 幾乎是完復制了 threading 的API, 因此只需花少量的時間就可以熟悉 threading 編程了。

一 起多進程

from multiprocessing import Process, current_process
import time


def func(i):
    time.sleep(2)
    proc = current_process()
    print("proce.name : ", proc.name)  # 輸出進程名
    print("proc.pid :", proc.pid)  # 輸出進程ID

if __name__ == "__main__":
    for i in range(10):
        p = Process(target=func, args=(i,))
        p.start()
        
# p.join() # p.join() 加上這一句之後,就是要等這個進程執行完之後才能進入下一個循環,也就是才能執行下一個進程,這樣就失去了多進程的意義

二 進程中再起線程

from multiprocessing import Process, current_process
import time
import threading


def foo():
    print("threading id is ", threading.get_ident())  # 獲取當前線和的ID


def func(i):
    time.sleep(2)
    proc 
= current_process() print("proce.name : ", proc.name) # 輸出進程名 print("proc.pid :", proc.pid) # 輸出進程ID p = threading.Thread(target=foo, ) # 進程中再起線程 p.start() if __name__ == "__main__": for i in range(10): p = Process(target=func, args=(i,)) p.start()

三 利用os模塊來查看各個進程ID

from multiprocessing import Process
import os


def info(title):
    print(title)
    print(module name:, __name__)
    print(parent process:, os.getppid())   #查看父進程ID
    print(process id:, os.getpid())  #查看當前進程ID
    print("\n\n")


def f(name):
    info(called from child process function f ) 
    print(hello, name)

if __name__ == __main__:
    info(main process line)
    p = Process(target=f, args=(bob,))
    p.start()
    # p.join()

結果如下: 可以看到,主進程的id就是子進程的父ID

技術分享圖片

四 進程間的數據傳遞---queue

from multiprocessing import Process, Queue
import threading


def f(q):
    q.put([42, None, hello])  # 子進程中put 一個值進入Queue


if __name__ == __main__:
    q = Queue()
    q.put("test123")  # 主進程中put 一個值進入Queue
    p = Process(target=f, args=(q,))
    p.start()
    p.join()
    print("444", q.get_nowait())
    print("444", q.get_nowait())

技術分享圖片

可以看到,在這個Queue中取出兩個值,所以在子進程中給這個Queue傳遞的值,在主進程中也可以取出來.

和多線程之間數據傳遞(共享比較)

import threading
import queue

def f():
    q.put([42, None, hello])

if __name__ == __main__:
    q = queue.Queue()
    q.put("test123")
    p = threading.Thread(target=f, )
    p.start()
    print("444", q.get_nowait())
    print("444", q.get_nowait())

結果是:

技術分享圖片

和進程數據傳遞比較:這裏不用傳遞這個queue,因為線程之間數據是共享的,在多進程中,如果不傳遞,則在子進程中就會報未定義的錯誤,

不能把線程queue作為參數傳給子進程

from multiprocessing import Process, Queue
import threading
import queue

def f(q):
    q.put([42, None, hello]) 


if __name__ == __main__:
    q =  queue.Queue()
    q.put("test123")  
    p = Process(target=f,args=(q,))
    p.start()
    print("444", q.get_nowait())
    print("444", q.get_nowait())

技術分享圖片

要想在進程之間傳遞數據,只能是進程queue,不能把線程queue作為參數傳給子進程.

五 進程間的數據傳遞---Pipe

from multiprocessing import Process, Pipe


def f(conn):
    conn.send([42, None, hello from child])
    print("",conn.recv()) # prints "from main"
    conn.close()

if __name__ == __main__:
    parent_conn, child_conn = Pipe() #產生兩個返回對象,分別代表兩頭
    p = Process(target=f, args=(child_conn,))
    p.start()
    print("parent",parent_conn.recv())  # prints "[42, None, ‘hello from child‘]"
    parent_conn.send("from main")  
    p.join()

生成一個Pipe 對象後就自動生成兩個返回對象,可以理解成兩頭,

也有兩個方法 send() 發送數據 recv()接收數據.

六 進程間的數據傳遞---manager

from multiprocessing import Process, Manager
import os

def f(dct, lst):
    d[os.getpid()] = os.getppid()
    l.append(os.getpid())
    # print(l)


if __name__ == __main__:
    with Manager() as manager:
        dct = manager.dict()  # {} #生成一個manager的字典(不是平常的字典),可在多個進程間共享和傳遞.
        lst = manager.list(range(5))  # 生成一個列表,可在多個進程間共享和傳遞
        p_list = []
        for i in range(10):
            p = Process(target=f, args=(dct, lst))
            p.start()
            p_list.append(p)
        for res in p_list:  # 等待所有進程執行完閉結果,這裏如如不寫就會報錯,說系統找不到指定文件
            res.join()
        print(dct)
        print(lst)

結果如下:

技術分享圖片

七 進程鎖

from multiprocessing import Process, Lock


def f(l, i):
    l.acquire() #加鎖
    print(hello world, i)
    l.release()  #解鎖


if __name__ == __main__:
    lock = Lock()
    for num in range(10):
        p=Process(target=f, args=(lock, num))
        p.start()
        # p.join() # 加上這個之後就要當前進程執行完之後才執行下一下進程

結果如下:

技術分享圖片

技術分享圖片

from multiprocessing import Process, Lock

def f(l, i):
    l.acquire() #加鎖
    print(hello world, i)
    l.release()  #解鎖


if __name__ == __main__:
    lock = Lock()
    for num in range(10):
        p=Process(target=f, args=(lock, num))
        p.start()
        p.join() # 加上這個之後就要當前進程執行完之後才執行下一下進程

結果如下

技術分享圖片

可以看到,打印的循序是固定的,而且在執行行明顯知道,他是上一個進程執行完之後才執行的下一個進程.

按道理說,各進程之間數據是獨立的,各進程之個對同一份內存數據是不共享的,(上面寫的那幾種數據共享都不是對同一份內存數據共享,只是復制了之後再共享的),不應該加鎖啊,但這裏各個進程是共享屏幕的,加鎖的目的主要是為了防止屏幕輸入輸出數據出錯.在python3中,進程鎖的意義不大.

八 進程池

from multiprocessing import Process, Pool, freeze_support
import time
import os


def Foo(i):
    time.sleep(2)
    print("in process", os.getpid())
    return i + 100

def Bar(arg):
    print(-->exec done:, arg, os.getpid())

if __name__ == __main__:  # 在windows 中必須寫在這個裏面,不然出錯
    # freeze_support()
    pool = Pool(processes=5)  # 允許進程池同時放入5個進程
    print("主進程", os.getpid())
    for i in range(10):
        pool.apply_async(func=Foo, args=(i,), callback=Bar)  # callback=回調函數,是指這個進程執行完之後要執行的函數
        # callback 函數是主進程調用的
        # pool.apply(func=Foo, args=(i,)) #串行
        # pool.apply_async(func=Foo, args=(i,)) #串行
    print(end)
    pool.close()  # 先close() 再join(),
    pool.join()  # 進程池中進程執行完畢後再關閉,如果註釋的話,當主程度執行完之後程序就關閉,而不會等各子進程執行完
    # 進程池的另一種用法,這種方法不能調用callback函數
    groups = [x * 20 for x in range(10)]
    pool = Pool(processes=5)
    pool.map(Foo, groups)

apply()是串行,也就是說只有等一個進程執行完才執行下一個進程,apply_async() 是並行,會按進程池中的個數一起執行,進程池的方法很多,用些方法不常用. map_async() 這是一個異常的map方法,可以調用callback()函數,用法和map一樣,只是是異步執行.

多進程學習總結