概念理解
對于操作系統(tǒng)來說晌砾,一個任務(wù)就是一個進(jìn)程。例如打開瀏覽器烦磁,打開word养匈,打開記事本等等,都是獨立的任務(wù)都伪,它們各自為一個或者多個進(jìn)程呕乎。這里要注意的是,同一種任務(wù)打開多個陨晶,分別屬于不同進(jìn)程猬仁,例如chrome打開多個標(biāo)簽,實際上它創(chuàng)建了多個進(jìn)程先誉。對于一個任務(wù)來說湿刽,它有很多子任務(wù),例如播放器褐耳,既要解碼視頻诈闺、也要解碼音頻,所以在進(jìn)程下存在多線程铃芦。在一個進(jìn)程下一定存在一個線程雅镊,可以稱它為主線程。操作系統(tǒng)創(chuàng)建進(jìn)程時刃滓,會單獨為每一個進(jìn)程分配各自的資源仁烹,進(jìn)程與進(jìn)程之間相互隔離。而進(jìn)程內(nèi)的線程咧虎,則共享了當(dāng)前進(jìn)程內(nèi)的資源晃危。可見,操作系統(tǒng)執(zhí)行的粒度是線程僚饭,分配資源的粒度是進(jìn)程震叮,我們的多任務(wù)操作系統(tǒng),在單核CPU上是在各個線程上不斷切換而達(dá)到目的鳍鸵,而在多核CPU上則能同時執(zhí)行多個線程任務(wù)苇瓣。Python能很方便地支持多進(jìn)程、多線程編程偿乖,接下來就簡單記錄下击罪,最后再記錄下兩者優(yōu)缺點。
在Linux系統(tǒng)下贪薪,有一個非常特殊的函數(shù)媳禁,fork()。它調(diào)用一次画切,返回兩次竣稽,操作系統(tǒng)自動把當(dāng)前進(jìn)程(父進(jìn)程)復(fù)制了一份(子進(jìn)程),然后分別在父進(jìn)程和子進(jìn)程內(nèi)返回霍弹。子進(jìn)程永遠(yuǎn)返回0毫别,父進(jìn)程返回子進(jìn)程的ID。經(jīng)過這樣做典格,父進(jìn)程就能fork出很多子進(jìn)程岛宦,并可以記錄下子進(jìn)程的ID號了,子進(jìn)程可以通過getppid()來獲取父進(jìn)程ID耍缴。fork()僅在Unix/Linux下使用砾肺,windows則不行。所以防嗡,在Python中债沮,存在一個跨平臺的包mutiprocessing,通過引入包中的Process類本鸣,就可以創(chuàng)建多進(jìn)程程序了疫衩,可以創(chuàng)建一個進(jìn)程p=Process(target=func,args=(*,)),然后利用p.start()及p.join()來執(zhí)行了荣德。以上的join()方法可以等待子進(jìn)程結(jié)束后才往下執(zhí)行闷煤,通常用于進(jìn)程間同步。另外涮瞻,可以用進(jìn)程池的方式鲤拿,例如p=Pool(n),然后p.apply_async(func,args)署咽,這里可以使用n種不同的參數(shù)傳入近顷,建立不同的進(jìn)程生音。用這種方式時,在調(diào)用join()方法前窒升,要先調(diào)用close()方法缀遍,使得不能再添加新進(jìn)程。mutiprocessing包里提供了Queue饱须、Pipe等多種進(jìn)程間通信的方法域醇。可以直接引入Queue類蓉媳,然后實例化一個對象譬挚。則不同的進(jìn)程可以使用put方法發(fā)信息,同時可以使用get方法取信息酪呻。
多個任務(wù)可以創(chuàng)建多個進(jìn)程來完成减宣,同時也可以創(chuàng)建多個線程來完成,線程是操作系統(tǒng)直接的執(zhí)行單元玩荠。Python含有threading這個高級模塊漆腌,要啟動一個線程,就是把一個函數(shù)傳出并創(chuàng)建Thread實例姨蟋,然后調(diào)用start()方法開始執(zhí)行,例如t=threading.Thread(target=func,name=*)立帖,注意這里的name屬性眼溶,它是給線程命名的,缺省值為Thread-1···晓勇。要注意的是堂飞,剛才說了,任何一個進(jìn)程都含有一個線程绑咱,而這個主線程則執(zhí)行著我們編寫的程序绰筛,可以調(diào)用threading.current_thread().name來查看它,它的名字就叫MainThread描融。在多線程編程中铝噩,有一個最大的問題就在于進(jìn)程內(nèi)的資源被各個線程所共享,進(jìn)程內(nèi)任何變量都可以被任何一個線程修改窿克,因此骏庸,線程之間若去修改同一個變量,則可能導(dǎo)致程序Bug年叮。所以具被,引入了鎖機制。當(dāng)某個線程去修改某個變量時只损,可以在變量所在的方法內(nèi)加一把鎖一姿,使得其他線程不能同時執(zhí)行該方法,只有釋放了鎖后,其他線程才能去獲得鎖并獲得修改權(quán)叮叹。創(chuàng)建一個鎖是通過lock=threading.Lock()來實現(xiàn)的艾栋,可以使用try···finally···語句,在try之前使用lock.acquire()獲得鎖衬横,然后在try語句里面修改變量裹粤,然后在finally語句里加lock.release()來保證鎖一定被釋放,避免成為一個死鎖蜂林。
多進(jìn)程的優(yōu)點是穩(wěn)定性好遥诉,一個子進(jìn)程崩潰了,不會影響主進(jìn)程以及其余進(jìn)程噪叙。但是缺點是創(chuàng)建進(jìn)程的代價非常大矮锈,因為操作系統(tǒng)要給每個進(jìn)程分配固定的資源,并且睁蕾,操作系統(tǒng)對進(jìn)程的總數(shù)會有一定的限制苞笨,若進(jìn)程過多,操作系統(tǒng)調(diào)度都會存在問題子眶,會造成假死狀態(tài)瀑凝。多線程優(yōu)點是效率較高一些,但是致命的缺點是任何一個線程崩潰都可能造成整個進(jìn)程的崩潰臭杰,因為它們共享了進(jìn)程的內(nèi)存資源池粤咪。對于任務(wù)數(shù)來說,無論是多進(jìn)程或者多線程渴杆,都不能太多寥枝。因為操作系統(tǒng)在切換任務(wù)時,會有一系列的保護(hù)現(xiàn)場措施磁奖,這要花費相當(dāng)?shù)南到y(tǒng)資源囊拜,若任務(wù)過多,則大部分資源都被用做干這些了比搭,結(jié)果就是所有任務(wù)都做不好冠跷,所以操作系統(tǒng)會限制進(jìn)程的數(shù)量。另外身诺,考慮計算密集型及IO密集型應(yīng)用程序蔽莱。對于計算密集型,多任務(wù)勢必造成資源浪費戚长。對于IO密集型盗冷,因為IO速度遠(yuǎn)低于CPU計算速度,所以使用多任務(wù)方式可以大大增大程序運行效率同廉。