python並發之multiprocessing

來源:互聯網
上載者:User

標籤:lte   通過   帶來   dict   添加   art   read   gpo   ret   

由於GIL(全域解釋鎖)的問題,python多線程並不能充分利用多核處理器。如果想要充分地使用多核CPU的資源,在python中大部分情況需要使用多進程。multiprocessing可以給每個進程賦予單獨的Python解譯器,這樣就規避了全域解釋鎖所帶來的問題。與threading.Thread類似,可以利用multiprocessing.Process對象來建立一個進程。multiprocessing支援子進程、通訊和共用資料、執行不同形式的同步,提供了Process、Queue、Pipe、Lock等組件。

Process單進程
# coding: utf-8from multiprocessing import Processimport timedef task(msg):    print ‘hello, %s‘ % msg    time.sleep(1)if __name__ == ‘__main__‘:    p = Process(target=task, args=(‘world‘,))    p.start()    if p.is_alive():        print ‘Process: %s is running‘ % p.pid    p.join()

 

這段代碼的執行過程:在主進程中建立子進程,然後調用start()啟動子進程,調用join()等待子進程執行完,再繼續執行主進程的整個的執行流程。 
控制子進程進入不同階段的是 start(), join(), is_alive(), terminate(), exitcode() 方法,這些方法只能在建立子進程的進程中執行。

建立:建立進程需要一個 function 和相關參數,參數可以是dictProcess(target=func, args=(), kwargs = {}),name 可以用來標識進程。

關閉:close停止接收新的任務,如果還有任務來,就會拋出異常。 join 是等待所有任務完成。 join 必須要在 close 之後調用,否則會拋出異常。

等待:在UNIX平台上,當某個進程終結之後,該進程需要被其父進程調用wait,否則進程成為殭屍進程(Zombie)。所以在這裡,我們調用了Process對象的join()方法 ,實際上等同於wait的作用。 
對於多線程來說,由於只有一個進程,所以不存在此必要性。

結束:terminate() 結束背景工作處理序,不再處理未完成的任務。

多進程
# coding: utf-8from multiprocessing import Processimport multiprocessingimport timedef task1(msg):    print ‘task1: hello, %s‘ % msg    time.sleep(1)def task2(msg):    print ‘task2: hello, %s‘ % msg    time.sleep(1)def task3(msg):    print ‘task3: hello, %s‘ % msg    time.sleep(1)if __name__ == ‘__main__‘:    p1 = Process(target=task1, args=(‘one‘,))    p2 = Process(target=task2, args=(‘two‘,))    p3 = Process(target=task3, args=(‘three‘,))    start = time.time()    p1.start()    p2.start()    p3.start()    print("The number of CPU is:" + str(multiprocessing.cpu_count()))    for p in multiprocessing.active_children():        print("child p.name: " + p.name + "\tp.id: " + str(p.pid))    p1.join()    p2.join()    p3.join()    end = time.time()    print(‘3 processes take %s seconds‘ % (end - start))

 

執行結果:

task1: hello, onetask2: hello, twotask3: hello, threeThe number of CPU is:4child p.name: Process-1:p.id: 99359child p.name: Process-2:p.id: 99360child p.name: Process-3:p.id: 993613 processes take 1.00933504105 seconds

 

這裡三個進程執行花費約1s,說明程式是並發執行的。對於更多的並發進程,我們可以放到一個迴圈中進行處理。

Lock

當多個進程需要訪問共用資源的時候,Lock可以用來避免訪問的衝突

# coding: utf-8from multiprocessing import Lock, Processimport timedef task1(lock, f):    with lock:        f = open(f, ‘w+‘)        f.write(‘hello ‘)        time.sleep(1)        f.close()def task2(lock, f):    lock.acquire()    try:        f = open(f, ‘a+‘)        time.sleep(1)        f.write(‘world!‘)    except Exception as e:        print(e)    finally:        f.close()        lock.release()if __name__ == ‘__main__‘:    lock = Lock()    fn = ‘./file.txt‘    start = time.time()    p1 = Process(target=task1, args=(lock, fn,))    p2 = Process(target=task2, args=(lock, fn,))    p1.start()    p2.start()    p1.join()    p2.join()    end = time.time()    print ‘time cost: %s seconds‘ % (end - start)    with open(fn, ‘r‘) as f:        for x in f.readlines():            print x

執行結果:

time cost: 2.0059568882 secondshello world!

因為要訪問共用檔案,先獲得鎖的進程會阻塞後面的進程,因此程式運行耗時約2s。

Semaphore

Semaphore 和 Lock 稍有不同,Semaphore 相當於 N 把鎖,擷取其中一把就可以執行了。 訊號量的總數 N 在構造時傳入,s = Semaphore(N)。 和 Lock 一樣,如果訊號量為0,則進程堵塞,直到訊號大於0。Semaphore可用來控制對共用資源的訪問數量,例如池的最大串連數。

# coding: utf-8from multiprocessing import Semaphore, Processimport timedef task(s, msg):    s.acquire()    print ‘hello, %s‘ % msg    time.sleep(1)    s.release()if __name__ == ‘__main__‘:    s = Semaphore(2)    processes = []    for x in range(8):        p = Process(target=task, args=(s, x,))        processes.append(p)    start = time.time()    for p in processes:        p.start()    for p in processes:        p.join()    end = time.time()    print ‘8 process takes %s seconds‘ % (end - start)

執行結果:

hello, 0hello, 1hello, 3hello, 2hello, 4hello, 5hello, 7hello, 68 process takes 4.00831484795 seconds

 

訊號量同步基於內部計數器,每調用一次acquire(),計數器減1;每調用一次release(),計數器加1.當計數器為0時,acquire()調用被阻塞。這是Dijkstra訊號量概念P()和V()的Python實現。訊號量同步機制適用於訪問像伺服器、檔案這樣的有限資源。

Event

Event用來實現進程間同步通訊。

# coding: utf-8from multiprocessing import Process, Eventimport timedef task1(e, msg):    print ‘task1 is waitting...‘    e.wait()    time.sleep(1)    print ‘hello, %s, e.is_set(): %s‘ % (msg, e.is_set())def task2(e, msg):    print ‘task2 is waitting...‘    e.wait(msg)    print ‘hello, %s, e.is_set(): %s‘ % (msg, e.is_set())if __name__ == ‘__main__‘:    e = Event()    p1 = Process(target=task1, args=(e, 1))    p2 = Process(target=task2, args=(e, 2))    p1.start()    p2.start()    time.sleep(3)    e.set()    print ‘main: event is set‘

 

執行結果:

task1 is waitting...task2 is waitting...hello, 2, e.is_set(): Falsemain: event is sethello, 1, e.is_set(): True

 

Pool

如果有50個task要執行,但 CPU 只有4核,我們當然可以迴圈建立50個進程來做這個事情,但這樣處理大大增加進程管理和調度的開銷。 
如果可以只建立4個進程,讓它們輪流工作完成任務,不用我們自己去管理具體的進程的建立、銷毀和調度,豈不更好。multiprocessing中的 Pool 可以協助我們做到這一點。

多進程非同步
# coding: utf-8from multiprocessing import Poolimport timedef task(msg):    print ‘hello, %s‘ % msg    time.sleep(1)if __name__ == ‘__main__‘:    pool = Pool(processes=4)    for x in range(10):        pool.apply_async(task, args=(x,))    pool.close()    pool.join()    print ‘processes done.‘

注意:這裡使用的是apply_async,多個進程非同步執行;如果調用async,就變成阻塞版本了。 
執行結果:

hello, 0hello, 1hello, 2hello, 3hello, 4hello, 5hello, 6hello, 7hello, 8hello, 910 processes take 3.09226489067 seconds

Pool 進程池建立4個進程,不管有沒有任務,都一直在進程池中等候。官網的描述:Worker processes within a Pool typically live for the complete duration of the Pool’s work queue。資料來的時候,若有空閑進程,則利用閒置進程完成任務,直到所有任務完成為止;若沒有閒置進程,則需要等待,直到池中有進程結束。

擷取進程執行結果

更多的時候,我們不僅需要多進程執行,還需要關注每個進程的執行結果,我們可以通過擷取apply_async的傳回值得到執行結果。

# coding: utf-8from multiprocessing import Poolimport timedef task(msg):    print ‘hello, %s‘ % msg    time.sleep(1)    return ‘msg: %s‘ % msgif __name__ == ‘__main__‘:    pool = Pool(processes=4)    results = []    for x in range(10):        ret = pool.apply_async(task, args=(x,))        results.append(ret)    pool.close()    pool.join()    print ‘processes done, result:‘    for x in results:        print x.get()

 

pool中的map方法

上面我們是通過一個迴圈往進程池新增工作,Pool提供了更優雅的map方法來管理工作的提交,只需對上面的代碼稍作修改。

# coding: utf-8from multiprocessing import Poolimport timedef task(msg):    print ‘hello, %s‘ % msg    time.sleep(1)    return ‘msg: %s‘ % msgif __name__ == ‘__main__‘:    pool = Pool(processes=4)    results = []    msgs = [x for x in range(10)]    results = pool.map(task, msgs)    pool.close()    pool.join()    print ‘processes done, result:‘    for x in results:        print x

python並發之multiprocessing

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.