Python多進程

多進程介紹

Python多線程無法利用CPU多核的優勢。因此在Python開發中,我們一般使用多進程進行並行開發。multiprocessing是類似於threading模塊的包。它支持了本地和遠程併發性,可以更充分的利用多核資源。

Process類

要運行一個進程需要創建實例化一個Process對象並且調用該類的start()方法。

`

from multiprocessing import Process

def greet(name):
    print('hello', name)

if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()`

Process([group [, target [, name [, args [, kwargs]]]]]),group默認爲None,不使用,參數target爲調用的對象,name爲子進程的名字,args爲調用對象的參數元組,kwargs爲調用對象的字典。

有關的方法和屬性

Process中的方法與threading.Thread中的十分相似。

run():進程啓動時運行的方法。

start():啓動進程,會調用子進程中的run()方法。

join([timeout]):主線程等待當前線程終止,timeout爲可選的超時時間,join只能join住start開啓的進程,而不能join住run開啓的進程。

terminate():強制終止進程,不會進行任何清理操作,如果創建了子進程,該子進程就成了殭屍進程。如果進程還保存了一個鎖那麼也將不會被釋放,進而導致死鎖。

is_alive():判斷進程是否在運行,爲bool值。

name:進程的名字,它是string類型,沒有語義,只是用於標誌進程,多進程中允許使用同一個名字。

dameon:必須在start()調用前設置,默認爲false,如果設爲True,代表當前進程爲後臺運行的守護進程,當前進程的父進程終止時,它也隨之終止,並且設定爲True後,不能創建自己的新進程。

pid:進程的id

每個進程還有特有的id號,可以通過os.getpid()得到當前進程的ID號,也可以直接使用p.pid得到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號

def greet(name):
    info('function greet')
    print('hello', name)

if __name__ == '__main__':
    info('main Process')
    p = Process(target=greet, args=('bob',))
    p.start()
    p.join()

進程間的通信

多進程支持兩種進程間通信的方式:隊列和管道。

隊列

隊列具有先進先出的特點,並且它是線程和進程安全的,通過q.put()方法將數據插入到隊列中,然後使用q.get()方法將數據取出。

from multiprocessing import Process, Queue

def greet(q):
    q.put("hello")

if __name__ == '__main__':
    q = Queue()
    p = Process(target=f, args=(q,))
    p.start()
    print(q.get())    # 打印hello 
    p.join()

管道

Pipe()函數返回一對雙向的連接對象,它們代表管道的兩端。每個連接對象有send()和recv()方法。當兩個進程或者線程試圖同時寫入或讀取管道的一端時,管道中的數據會被損壞,當然同一時刻使用不同的管道端口不會有損壞的風險。

from multiprocessing import Process, Pipe

def greet(p):
    p.send("hello")
    p.close()

if __name__=='__main__':
    left_p,right_p=Pipe()
    p=Process(target=greet,args=(right_p,))
    p.start()
    print(left_p.recv()) #輸出hello
    p.join()

進程間的同步

多進程包含與多線程中等價的同步原語。例如可以使用鎖機制確保某一時刻只有一個進程打印到標準輸出。

from multiprocessing import Process, Lock

def greet(lock,num):
    lock.acquire()  #獲取鎖
    print("hello",num)
    lock.release()  #將鎖釋放

if __name__=='__main__':
    lock=Lock()
    for num in range(5):
        Process(target=greet,args=(lock,num),).start()

進程間分享狀態

在進行併發編程時,最好儘量避免使用共享狀態,尤其是使用多進程時。但是,如果你確實要使用一些共享的數據,多進程也提供一些方法。

共享內存

數組可以利用Value和Array存儲在共享內存映射中。

from multiprocessing import Process, Value, Array

def share(val,arr):
    val.value=1
    for i in range(len(arr)):
        arr[i]=arr[i]+1

if __name__=='__main__':
    val=Value('d',0.0)
    arr=Array('i',range(10))

    p=Process(target=share,args=(val,arr))
    p.start()
    p.join()

    print(val.value)
    print(arr[:])

輸出值爲:

`1.0
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

`

其中d和i分別代表創建Value和Array中存儲的數字類型爲double和signed integer。這些共享對象都是線程和進程安全的。

服務器進程

通過Manager()創建一個管理類,它可以控制一個服務器進程,這些服務器進程持有Python對象,並且允許其他進程利用代理來操縱Python對象。

from multiprocessing import Process, Manager
def func(dic,li):
    dic['hello']=1
    dic[1]='Bob'
    dic[2.01]=None
    li.reverse()

if __name__=='__main__':

    manager=Manager() 
    dic=manager.dict()
    li=manager.list(range(5))

    p=Process(target=func,args=(dic,li))
    p.start()
    p.join()
    print(dic)
    print(li)

輸出結果爲:

{'hello': 1, 1: 'Bob', 2.01: None}
[4, 3, 2, 1, 0]

服務器進程管理比使用共享內存對象更加的靈活,因爲它可以支持任意類型的對象。manager也可以通過進程進行共享,只不過比使用共享內存慢。

進程池

進程池中有許多進程,它有方法讓進程池中的進程以不同的方式運行任務。 可以創建一個池池,這些進程將執行池類向它提交的任務。
多進程編程並不是進程越多越好,還與CPU核數有關,進程數過多反而會降低效率。

Pool([processes[, initializer[, initargs[, maxtasksperchild]]]])

相關方法

apply(func[, args[, kwds]]):在一個池工作進程中執行func(*args,**kwargs),然後返回結果。需要強調的是:此操作並不會在所有池工作進程中並執行func函數。如果要通過不同參數併發地執行func函數,必須從不同線程調用p.apply()函數或者使用
p.apply_async()

apply_async(func [, args [, kwargs]]):在一個池工作進程中執func(*args,**kwargs),然後返回結果。此方法的結果是AsyncResult類的實例,callback是可調用對象,接收輸入參數。當func的結果變爲可用時,將理解傳遞給callback。callback禁止執行任何阻塞操作,否則將接收其他異步操作中的結果。

close():關閉進程池。如果所有操作持續掛起,它們將在工作進程終止前完成。

P.join():等待所有工作進程退出。此方法只能在close()或teminate()之後調用

from multiprocessing import Pool

def sqr(x):
    return x*x

if __name__=='__main__':
    pool=Pool(processes=5) #開啓5個工作進程
    result=pool.apply_async(sqr,[10]) #異步計算sqr(10))
    print(result.get(timeout=1)) #輸出100
    print(pool.map(sqr,range(5))) #輸出[0,1,4.....16]

參考

https://www.cnblogs.com/smallmars/p/7093603.html
http://docspy3zh.readthedocs.io/en/latest/library/multiprocessing.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章