python多進(jìn)程和多線程介紹
一、什么是進(jìn)程和線程
進(jìn)程是分配資源的最小單位,線程是系統(tǒng)調(diào)度的最小單位。
當(dāng)應(yīng)用程序運(yùn)行時(shí)最少會(huì)開啟一個(gè)進(jìn)程,此時(shí)計(jì)算機(jī)會(huì)為這個(gè)進(jìn)程開辟獨(dú)立的內(nèi)存空間,不同的進(jìn)程享有不同的空間,而一個(gè)CPU在同一時(shí)刻只能夠運(yùn)行一個(gè)進(jìn)程,其他進(jìn)程處于等待狀態(tài)。
一個(gè)進(jìn)程內(nèi)部包括一個(gè)或者多個(gè)線程,這些線程共享此進(jìn)程的內(nèi)存空間與資源。相當(dāng)于把一個(gè)任務(wù)又細(xì)分成若干個(gè)子任務(wù),每個(gè)線程對(duì)應(yīng)一個(gè)子任務(wù)。
二、多進(jìn)程和多線程
對(duì)于一個(gè)CPU來說,在同一時(shí)刻只能運(yùn)行一個(gè)進(jìn)程或者一個(gè)線程,而單核CPU往往是在進(jìn)程或者線程間切換執(zhí)行,每個(gè)進(jìn)程或者線程得到一定的CPU時(shí)間,由于切換的速度很快,在我們看來是多個(gè)任務(wù)在并行執(zhí)行(同一時(shí)刻多個(gè)任務(wù)在執(zhí)行),但實(shí)際上是在并發(fā)執(zhí)行(一段時(shí)間內(nèi)多個(gè)任務(wù)在執(zhí)行)。
單核CPU的并發(fā)往往涉及到進(jìn)程或者線程的切換,進(jìn)程的切換比線程的切換消耗更多的時(shí)間與資源。在單核CPU下,CPU密集的任務(wù)采用多進(jìn)程或多線程不會(huì)提升性能,而在IO密集的任務(wù)中可以提升(IO阻塞時(shí)CPU空閑)。
而多核CPU就可以做到同時(shí)執(zhí)行多個(gè)進(jìn)程或者多個(gè)進(jìn)程,也就是并行運(yùn)算。在擁有多個(gè)CPU的情況下,往往使用多進(jìn)程或者多線程的模式執(zhí)行多個(gè)任務(wù)。
三、python中的多進(jìn)程和多線程
1、多進(jìn)程
def Test(pid): ? ? print("當(dāng)前進(jìn)程{}:{}".format(pid, os.getpid())) ? ? for i in range(1000000000): ? ? ? ? pass if __name__ == '__main__': ? ? #單進(jìn)程 ? ? start = time.time() ? ? for i in range(2): ? ? ? ? Test(i) ? ? end = time.time() ? ? print((end - start))
單進(jìn)程輸出結(jié)果如圖:
def Test(pid): ? ? print("當(dāng)前子進(jìn)程{}:{}".format(pid, os.getpid())) ? ? for i in range(100000000): ? ? ? ? pass if __name__ == '__main__': ? ? #多進(jìn)程 ? ? print("父進(jìn)程:{}".format(os.getpid())) ? ? start = time.time() ? ? pool = Pool(processes=2) ? ? pid = [i for i in range(2)] ? ? pool.map(Test, pid) ? ? pool.close() ? ? pool.join() ? ? end = time.time() ? ? print((end - start))
多進(jìn)程輸出結(jié)果如圖:
從輸出結(jié)果可以看出都是執(zhí)行兩次for循環(huán),多進(jìn)程比單進(jìn)程減少了近乎一半的時(shí)間(這里使用了兩個(gè)進(jìn)程),并且查看CPU情況可以看出多進(jìn)程利用了多個(gè)CPU。
python中的多進(jìn)程可以利用mulitiprocess
模塊的Pool類創(chuàng)建,利用Pool的map方法來運(yùn)行子進(jìn)程。
一般多進(jìn)程的執(zhí)行如下代碼:
def Test(pid): ? ? print("當(dāng)前子進(jìn)程{}:{}".format(pid, os.getpid())) ? ? for i in range(100000000): ? ? ? ? pass if __name__ == '__main__': ? ? #多進(jìn)程 ? ? print("父進(jìn)程:{}".format(os.getpid())) ? ? pool = Pool(processes=2) ? ? pid = [i for i in range(4)] ? ? pool.map(Test, pid) ? ? pool.close() ? ? pool.join()
1、利用Pool類創(chuàng)建一個(gè)進(jìn)程池,processes
聲明在進(jìn)程池中最多可以運(yùn)行幾個(gè)子進(jìn)程,不聲明的情況下會(huì)自動(dòng)根據(jù)CPU數(shù)量來設(shè)定,原則上進(jìn)程池容量不超過CPU數(shù)量。(出于資源的考慮,不要?jiǎng)?chuàng)建過多的進(jìn)程)
2、聲明一個(gè)可迭代的變量,該變量的長(zhǎng)度決定要執(zhí)行多少次子進(jìn)程。
3、利用map()方法執(zhí)行多進(jìn)程,map方法兩個(gè)參數(shù),第一個(gè)參數(shù)是多進(jìn)程執(zhí)行的方法名,第二個(gè)參數(shù)是第二步聲明的可迭代變量,里面的每一個(gè)元素是方法所需的參數(shù)。 這里需要注意幾個(gè)點(diǎn):1)進(jìn)程池滿的時(shí)候請(qǐng)求會(huì)等待,以上述代碼為例,聲明了一個(gè)容量為2的進(jìn)程池,但是可迭代變量有4個(gè),那么在執(zhí)行的時(shí)候會(huì)先創(chuàng)建兩個(gè)子進(jìn)程,此時(shí)進(jìn)程池已滿,等待有子進(jìn)程執(zhí)行完成,才繼續(xù)處理請(qǐng)求;
2) 子進(jìn)程處理完一個(gè)請(qǐng)求后,會(huì)利用已經(jīng)創(chuàng)建好的子進(jìn)程繼續(xù)處理新的請(qǐng)求而不會(huì)重新創(chuàng)建進(jìn)程。
從圖3可以看出上述兩個(gè)點(diǎn),如果同時(shí)處理4個(gè)進(jìn)程,那么只需要2秒鐘,這里是分成兩次處理,花費(fèi)了4秒,并且兩次處理使用的子進(jìn)程號(hào)都相同。
3)map會(huì)將每個(gè)子進(jìn)程的返回值匯總成一個(gè)列表返回。
4、在所有請(qǐng)求處理結(jié)束后使用close()方法關(guān)閉進(jìn)程池不再接受請(qǐng)求。
5、使用join()方法讓主進(jìn)程阻塞,等待子進(jìn)程退出,join()
方法要放在close()
方法之后,防止主進(jìn)程在子進(jìn)程結(jié)束之前退出。
2、多線程
python的多線程模塊用threading類進(jìn)行創(chuàng)建
import time import threading import os count = 0 def change(n): ? ? global count ? ? count = count + n ? ? count = count - n def run(n): ? ? print("當(dāng)前子線程:{}".format(threading.current_thread().name)) ? ? for i in range(10000000): ? ? ? ? change(n) if __name__ == '__main__': ? ? print("主線程:{}".format(threading.current_thread().name)) ? ? thread_1 = threading.Thread(target=run, args=(3,)) ? ? thread_2 = threading.Thread(target=run, args=(10,)) ? ? thread_1.start() ? ? thread_2.start() ? ? thread_1.join() ? ? thread_2.join() ? ? print(count)
程序執(zhí)行會(huì)創(chuàng)建一個(gè)進(jìn)程,進(jìn)程會(huì)默認(rèn)啟動(dòng)一個(gè)主線程,使用threading.Thread()創(chuàng)建子線程;target為要執(zhí)行的函數(shù);args傳入函數(shù)需要的參數(shù);start()啟動(dòng)子線程,join()阻塞主線程先運(yùn)行子線程。 由于變量由多個(gè)線程共享,任何一個(gè)線程都可以對(duì)于變量進(jìn)行修改,如果同時(shí)多個(gè)線程修改變量就會(huì)出現(xiàn)錯(cuò)誤。
上面的程序在理論上的結(jié)果應(yīng)該為0,但運(yùn)行結(jié)果如圖:
出現(xiàn)這個(gè)結(jié)果的原因就是多個(gè)線程同時(shí)對(duì)于變量修改,在賦值時(shí)出現(xiàn)錯(cuò)誤,具體解釋見多線程
解決這個(gè)問題就是在修改變量的時(shí)候加鎖,這樣就可以避免出現(xiàn)多個(gè)線程同時(shí)修改變量。
import time import threading import os count = 0 lock = threading.Lock() def change(n): ? ? global count ? ? count = count + n ? ? count = count - n def run(n): ? ? print("當(dāng)前子線程:{}".format(threading.current_thread().name)) ? ? for i in range(10000000): ? ? ? ? # lock.acquire() ? ? ? ? # try: ? ? ? ? ? ? change(n) ? ? ? ? # finally: ? ? ? ? # ? ? lock.release() if __name__ == '__main__': ? ? print("主線程:{}".format(threading.current_thread().name)) ? ? thread_1 = threading.Thread(target=run, args=(3,)) ? ? thread_2 = threading.Thread(target=run, args=(10,)) ? ? thread_1.start() ? ? thread_2.start() ? ? thread_1.join() ? ? thread_2.join() ? ? print(count)
python中的線程需要先獲取GIL(Global Interpreter Lock)鎖才能繼續(xù)運(yùn)行,每一個(gè)進(jìn)程僅有一個(gè)GIL,線程在獲取到GIL之后執(zhí)行100字節(jié)碼或者遇到IO中斷時(shí)才會(huì)釋放GIL,這樣在CPU密集的任務(wù)中,即使有多個(gè)CPU,多線程也是不能夠利用多個(gè)CPU來提高速率,甚至可能會(huì)因?yàn)楦?jìng)爭(zhēng)GIL導(dǎo)致速率慢于單線程。所以對(duì)于CPU密集任務(wù)往往使用多進(jìn)程,IO密集任務(wù)使用多線程。
到此這篇關(guān)于python多進(jìn)程和多線程介紹的文章就介紹到這了,更多相關(guān)python多進(jìn)程和多線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python連接達(dá)夢(mèng)數(shù)據(jù)庫(kù)的實(shí)現(xiàn)示例
本文主要介紹了Python連接達(dá)夢(mèng)數(shù)據(jù)庫(kù)的實(shí)現(xiàn)示例,dmPython是DM提供的依據(jù)Python DB API version 2.0中API使用規(guī)定而開發(fā)的數(shù)據(jù)庫(kù)訪問接口,使Python應(yīng)用程序能夠?qū)M數(shù)據(jù)庫(kù)進(jìn)行訪問2023-12-12一篇文章搞懂Python程序流程控制結(jié)構(gòu)
這篇文章主要給大家介紹了關(guān)于Python程序流程控制結(jié)構(gòu)的相關(guān)資料,本節(jié)學(xué)習(xí)了Python程序的控制結(jié)構(gòu)之順序結(jié)構(gòu)、分支結(jié)構(gòu)、循環(huán)結(jié)構(gòu),文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09Python內(nèi)建類型float源碼學(xué)習(xí)
這篇文章主要為大家介紹了Python內(nèi)建類型float源碼學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05Python3實(shí)現(xiàn)發(fā)送QQ郵件功能(附件)
這篇文章主要為大家詳細(xì)介紹了Python3實(shí)現(xiàn)發(fā)送QQ郵件功能,附件方面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12python實(shí)現(xiàn)機(jī)器學(xué)習(xí)之多元線性回歸
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)機(jī)器學(xué)習(xí)之多元線性回歸,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-09-09聊聊PyTorch中eval和no_grad的關(guān)系
這篇文章主要介紹了聊聊PyTorch中eval和no_grad的關(guān)系,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-05-05