多進程學習總結

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

一 起多進程

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傳遞的值,在主進程中也能夠取出來.windows

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

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,由於線程之間數據是共享的,在多進程中,若是不傳遞,則在子進程中就會報未定義的錯誤,app

不能把線程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 對象後就自動生成兩個返回對象,能夠理解成兩頭,async

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

六 進程間的數據傳遞---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同樣,只是是異步執行.

相關文章
相關標籤/搜索