操作系統(tǒng)
- 進(jìn)程管理
- 進(jìn)程
- 線程
- 協(xié)程
- 多線程資源搶占
- 多線程死鎖
- Python GIL (Global Interpreter Lock) 問題
- 全局解釋器鎖
- 用多進(jìn)程規(guī)避這個(gè)問題
- 雖說是假多線程,但是能用來做 io 依賴的任務(wù)還是可以的
- 并發(fā)并行
- 內(nèi)存管理
- 內(nèi)存泄漏
- 自動(dòng)內(nèi)存管理(GC 垃圾回收)
- 循環(huán)引用
- 非托管資源
- 驅(qū)動(dòng)程序(硬件管理)
- 文件系統(tǒng)(馮諾依曼結(jié)構(gòu)的電腦實(shí)際上可以不包含外存)
- 操作系統(tǒng):
驅(qū)動(dòng)(硬件的抽象)-》
進(jìn)程(操作系統(tǒng)的抽象,獨(dú)立的資源和內(nèi)存)-》
線程 (更容易資源共享,更容易使用多核)
一、進(jìn)程管理
??qq、pycharm、firefox
等程序都是獨(dú)立的進(jìn)程,一個(gè)程序就是一個(gè)進(jìn)程,管理進(jìn)程就是管理在操作系統(tǒng)中運(yùn)行的程序。
??為什么會(huì)有進(jìn)程呢?
??先有電腦,然后在電腦上寫程序,程序是和電腦綁死的,如果電腦硬件更新,就需要重新寫代碼,所以出現(xiàn)了操作系統(tǒng),將代碼置于操作系統(tǒng)之上。更新驅(qū)動(dòng),就可以跑運(yùn)行在不同的硬件上,不需要改代碼。
??以前的電腦是單任務(wù),一次只能執(zhí)行一個(gè)程序。后來需要在電腦上運(yùn)行多個(gè)程序,這些程序自己管理自己,保證別人不會(huì)搶占你的內(nèi)存空間,不讓別人把你關(guān)掉等等。后來操作系統(tǒng)進(jìn)化出了進(jìn)程的概念,相當(dāng)于是虛擬的操作系統(tǒng)的抽象,在進(jìn)程里面有一份虛擬的內(nèi)存空間和執(zhí)行順序。操作系統(tǒng)是對(duì)硬件的一個(gè)抽象,那么進(jìn)程就相當(dāng)于對(duì)操作系統(tǒng)的抽象。進(jìn)程就變成了基本的運(yùn)行單元,進(jìn)程之間相互隔離,數(shù)據(jù)不能共享。
??進(jìn)程調(diào)度由操作系統(tǒng)進(jìn)行控制,操作系統(tǒng)內(nèi)部會(huì)有一張表,表里面有進(jìn)程id(pid)以及進(jìn)程的地址和運(yùn)行狀態(tài),那么操作系統(tǒng)就可以根據(jù)這張表對(duì)其調(diào)度。
??單核在物理成面一次只能執(zhí)行一個(gè)程序,但是它的計(jì)算速度非常快,可以輪流執(zhí)行(時(shí)間分片)不同的程序,所以人們感覺程序在同時(shí)執(zhí)行。
??為什么會(huì)有線程呢?
??最開始單進(jìn)程只是運(yùn)行在單核上面,出現(xiàn)多核后,讓一個(gè)程序完全占領(lǐng)這些核很麻煩,因?yàn)槿绻邪藗€(gè)核,開八個(gè)進(jìn)程,這些進(jìn)程的數(shù)據(jù)是相互隔離的,不方便交互訪問。就出現(xiàn)了線程,線程可以同時(shí)跑在不同的核里面,充分利用核資源,還可以共享數(shù)據(jù)(共享變量)。進(jìn)程之間的通信類似于瀏覽器與服務(wù)器之間的通信差不多,需要進(jìn)過轉(zhuǎn)義等。進(jìn)程之間數(shù)據(jù)的共享在數(shù)據(jù)庫(kù)或redies;
??線程是進(jìn)程的進(jìn)一步劃分,線程之間的數(shù)據(jù)是共享的。線程的調(diào)度也是操作系統(tǒng)進(jìn)行的,由于線程的資源是共享的,那么就可能導(dǎo)致?lián)屨假Y源的問題或死鎖。
線程 進(jìn)程
對(duì)于四核cpu,程序可以開四個(gè)線程,每個(gè)核上跑一個(gè)線程,這樣四個(gè)線程可以同時(shí)運(yùn)行。如果開八個(gè)線程,每個(gè)核上兩個(gè),那么這兩個(gè)就是按照時(shí)間片段來執(zhí)行。
進(jìn)程:一個(gè)執(zhí)行的程序
線程:程序的子操作(視頻不僅有畫面還有聲音)
處理多線程就是異步,單線程就是同步
同步是阻塞模式,異步是非阻塞模式。
簡(jiǎn)單比喻:
http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
https://www.liaoxuefeng.co/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319272686365ec7ceaeca33428c914edf8f70cca383000
- 定義
進(jìn)程:具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位。程序是一個(gè)沒有生命的實(shí)體,只有處理器賦予程序生命時(shí),它才能成為一個(gè)活動(dòng)的實(shí)體,線程是其活動(dòng)的小單位。
線程:進(jìn)程的一個(gè)實(shí)體,在引入線程的操作系統(tǒng)中,通常都是把進(jìn)程作為分配資源的基本單位,而把線程作為獨(dú)立運(yùn)行和獨(dú)立調(diào)度的基本單位。線程自己不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源(如程序計(jì)數(shù)器,一組寄存器和棧),但是它可與同屬一個(gè)進(jìn)程的其他的線程共享進(jìn)程所擁有的全部資源。
??多線程主要是為了節(jié)約CPU時(shí)間,發(fā)揮利用,根據(jù)具體情況而定。線程的運(yùn)行中需要使用計(jì)算機(jī)的內(nèi)存資源和CPU。
關(guān)系
一個(gè)線程可以創(chuàng)建和撤銷另一個(gè)線程;同一個(gè)進(jìn)程中的多個(gè)線程之間可以并發(fā)執(zhí)行。
相對(duì)進(jìn)程而言,線程是一個(gè)更加接近于執(zhí)行體的概念,它可以與同進(jìn)程中的其他線程共享數(shù)據(jù),但擁有自己的棧空間,擁有獨(dú)立的執(zhí)行序列。區(qū)別
進(jìn)程和線程的主要差別在于它們是不同的操作系統(tǒng)資源管理方式。進(jìn)程有獨(dú)立的地址空間,一個(gè)進(jìn)程崩潰后,在保護(hù)模式下不會(huì)對(duì)其它進(jìn)程產(chǎn)生影響,而線程只是一個(gè)進(jìn)程中的不同執(zhí)行路徑。線程有自己的堆棧和局部變量,但線程之間沒有單獨(dú)的地址空間,一個(gè)線程死掉就等于整個(gè)進(jìn)程死掉,所以多進(jìn)程的程序要比多線程的程序健壯,但在進(jìn)程切換時(shí),耗費(fèi)資源較大,效率要差一些。但對(duì)于一些要求同時(shí)進(jìn)行并且又要共享某些變量的并發(fā)操作,只能用線程,不能用進(jìn)程。
- 簡(jiǎn)而言之,一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程.
- 線程的劃分尺度小于進(jìn)程,基本上不擁有系統(tǒng)資源,故對(duì)它的調(diào)度所付出的開銷就會(huì)小得多,能更高效的提高系統(tǒng)內(nèi)多個(gè)程序間并發(fā)執(zhí)行的程度。使得多線程程序的并發(fā)性高。
- 另外,進(jìn)程在執(zhí)行過程中擁有獨(dú)立的內(nèi)存單元,而多個(gè)線程共享內(nèi)存,從而極大地提高了程序的運(yùn)行效率。進(jìn)程間相互獨(dú)立,同一進(jìn)程的各線程間共享。某進(jìn)程內(nèi)的線程在其它進(jìn)程不可見。
- 線程在執(zhí)行過程中與進(jìn)程還是有區(qū)別的。每個(gè)獨(dú)立的線程有一個(gè)程序運(yùn)行的入口、順序執(zhí)行序列和程序的出口。但是線程不能夠獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個(gè)線程執(zhí)行控制。
- 從邏輯角度來看,多線程的意義在于一個(gè)應(yīng)用程序中,有多個(gè)執(zhí)行部分可以同時(shí)執(zhí)行。但操作系統(tǒng)并沒有將多個(gè)線程看做多個(gè)獨(dú)立的應(yīng)用,來實(shí)現(xiàn)進(jìn)程的調(diào)度和管理以及資源分配。這就是進(jìn)程和線程的重要區(qū)別。
- 優(yōu)缺點(diǎn)
線程和進(jìn)程在使用上各有優(yōu)缺點(diǎn):線程執(zhí)行開銷小,但不利于資源的管理和保護(hù);而進(jìn)程正相反。同時(shí),線程適合于在SMP機(jī)器上運(yùn)行,而進(jìn)程則可以跨機(jī)器遷移;
加鎖和死鎖
??對(duì)于代碼 A 和 代碼 B,如果先執(zhí)行代碼 A,再執(zhí)行代碼 B,會(huì)得到我們想要的結(jié)果,如果開多線程,而且在 A 內(nèi)設(shè)置時(shí)間暫停,那么就會(huì)中斷執(zhí)行 A 去執(zhí)行 B,那么最終結(jié)果就會(huì)變化。
加鎖:
??如果兩段代碼A和B(加時(shí)間停頓),如果必須執(zhí)行完A后,才能執(zhí)行B,執(zhí)行完B后,接著執(zhí)行A,那么可以給A和B分別加鎖,那么代碼 A、B 就不能被分開執(zhí)行了(原子性),這樣可以保證執(zhí)行的順序,如果搞不好就會(huì)造成死鎖。
死鎖:
如果在代碼A、B內(nèi)加鎖后沒有釋放鎖,那么執(zhí)行A期間會(huì)執(zhí)行B,執(zhí)行B期間會(huì)執(zhí)行A,但是內(nèi)有釋放鎖,所以不能交叉執(zhí)行,就會(huì)出現(xiàn)死鎖現(xiàn)象。
python GIL(Global Interpreter Lock)
全局解釋器鎖
??python 雖然是多線程程序,但有個(gè)全局解釋器鎖,會(huì)導(dǎo)致同一時(shí)刻只能有一個(gè)線程在執(zhí)行。
理想的多線程:
python的多線程:每次實(shí)際只有一個(gè)線程再執(zhí)行(縱軸是時(shí)間)
??多線程比較難寫,容易資源搶占或死鎖,所以用 GIL 規(guī)避這些問題。那么既然不能享受多線程帶來的遍歷,在多核電腦下,怎么充分利用這些核呢,采用多進(jìn)程!開多個(gè)進(jìn)程可以在不同的核上面執(zhí)行。雖然是假線程,但還是有一定作用的。
并發(fā)和并行
??你吃飯吃到一半,電話來了,你一直到吃完了以后才去接,這就說明你不支持并發(fā)也不支持并行。你吃飯吃到一半,電話來了,你停了下來接了電話,接完后繼續(xù)吃飯,這說明你支持并發(fā)。你吃飯吃到一半,電話來了,你一邊打電話一邊吃飯,這說明你支持并行。并發(fā)的關(guān)鍵是你有處理多個(gè)任務(wù)的能力,不一定要同時(shí)。并行的關(guān)鍵是你有同時(shí)處理多個(gè)任務(wù)的能力。所以我認(rèn)為它們最關(guān)鍵的點(diǎn)就是:是否是『同時(shí)』。
??當(dāng)有多個(gè)線程在操作時(shí),如果系統(tǒng)只有一個(gè)CPU,則它根本不可能真正同時(shí)進(jìn)行一個(gè)以上的線程,它只能把CPU運(yùn)行時(shí)間劃分成若干個(gè)時(shí)間段,再將時(shí)間段分配給各個(gè)線程執(zhí)行,在一個(gè)時(shí)間段的線程代碼運(yùn)行時(shí),其它線程處于掛起狀態(tài),這種方式我們稱之為并發(fā) (Concurrent
)。
??當(dāng)系統(tǒng)有一個(gè)以上CPU時(shí),則線程的操作有可能非并發(fā).當(dāng)一個(gè)CPU執(zhí)行一個(gè)線程時(shí),另一個(gè)CPU可以執(zhí)行另一個(gè)線程,兩個(gè)線程互不搶占CPU資源,可以同時(shí)進(jìn)行,這種方式我們稱之為并行 (Parallel
)。
http://blog.csdn.net/java_zero2one/article/details/51477791
線程安全
??線程安全問題都是由全局變量及靜態(tài)變量引起的。若每個(gè)線程中對(duì)全局變量、靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個(gè)全局變量是線程安全的;若有多個(gè)線程同時(shí)執(zhí)行寫操作,都需要考慮線程同步,否則的話就可能影響線程安全。
??多線程訪問時(shí),采用了加鎖機(jī)制,當(dāng)一個(gè)線程訪問該類的某個(gè)數(shù)據(jù)時(shí),進(jìn)行保護(hù),其他線程不能進(jìn)行訪問直到該線程讀取完,其他線程才可使用。不會(huì)出現(xiàn)數(shù)據(jù)不一致或者數(shù)據(jù)污染。 線程不安全就是不提供數(shù)據(jù)訪問保護(hù),有可能出現(xiàn)多個(gè)線程先后更改數(shù)據(jù)造成所得到的數(shù)據(jù)是臟數(shù)據(jù)。
比如一個(gè) ArrayList 類,在添加一個(gè)元素的時(shí)候,它可能會(huì)有兩步來完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。
在單線程運(yùn)行的情況下,如果 Size = 0,添加一個(gè)元素后,此元素在位置 0,而且 Size=1;
而如果是在多線程情況下,比如有兩個(gè)線程,線程 A 先將元素存放在位置 0。但是此時(shí) CPU 調(diào)度線程A暫停,線程 B 得到運(yùn)行的機(jī)會(huì)。線程B也向此 ArrayList 添加元素,因?yàn)榇藭r(shí) Size 仍然等于 0 (注意哦,我們假設(shè)的是添加一個(gè)元素是要兩個(gè)步驟哦,而線程A僅僅完成了步驟1),所以線程B也將元素存放在位置0。然后線程A和線程B都繼續(xù)運(yùn)行,都增加 Size 的值。
那好,我們來看看 ArrayList 的情況,元素實(shí)際上只有一個(gè),存放在位置 0,而 Size 卻等于 2。這就是“線程不安全”了。
協(xié)程
- 一開始大家想要同一時(shí)間執(zhí)行那么三五個(gè)程序,大家能一塊跑一跑。特別是
UI
什么的,別一上計(jì)算量比較大的玩意就跟死機(jī)一樣。于是就有了并發(fā),從程序員的角度可以看成是多個(gè)獨(dú)立的邏輯流。內(nèi)部也可以是多cpu
并行,也可以是單cpu
時(shí)間分片,能快速的切換邏輯流,看起來像是大家一塊跑的就行。 - 但是一塊跑就有問題了。我計(jì)算到一半,剛把多次方程解到最后一步,你突然插進(jìn)來,我的中間狀態(tài)咋辦,我用來儲(chǔ)存的內(nèi)存被你覆蓋了咋辦?所以跑在一個(gè) cpu 里面的并發(fā)都需要處理上下文切換的問題。進(jìn)程就是這樣抽象出來個(gè)一個(gè)概念,搭配虛擬內(nèi)存、進(jìn)程表之類的東西,用來管理獨(dú)立的程序運(yùn)行、切換。
- 后來電腦上有了好幾個(gè) cpu,大家都別閑著,一人跑一進(jìn)程。就是所謂的并行。
- 因?yàn)槌绦虻氖褂蒙婕按罅康挠?jì)算機(jī)資源配置,把這活隨意的交給用戶程序,非常容易讓整個(gè)系統(tǒng)分分鐘被搞跪,資源分配也很難做到相對(duì)的公平。所以核心的操作需要陷入內(nèi)核 (kernel),切換到操作系統(tǒng),讓老大幫你來做。
- 有的時(shí)候碰著 I/O 訪問,阻塞了后面所有的計(jì)算。空著也是空著,老大就直接把 CPU 切換到其他進(jìn)程,讓人家先用著。當(dāng)然除了 I\O 阻塞,還有時(shí)鐘阻塞等等。一開始大家都這樣弄,后來發(fā)現(xiàn)不成,太慢了。為啥呀,一切換進(jìn)程得反復(fù)進(jìn)入內(nèi)核,置換掉一大堆狀態(tài)。進(jìn)程數(shù)一高,大部分系統(tǒng)資源就被進(jìn)程切換給吃掉了。后來搞出線程的概念,大致意思就是,這個(gè)地方阻塞了,但我還有其他地方的邏輯流可以計(jì)算,這些邏輯流是共享一個(gè)地址空間的,不用特別麻煩的切換頁(yè)表、刷新 TLB,只要把寄存器刷新一遍就行,能比切換進(jìn)程開銷少點(diǎn)。
- 如果連時(shí)鐘阻塞、 線程切換這些功能我們都不需要了,自己在進(jìn)程里面寫一個(gè)邏輯流調(diào)度的東西。那么我們即可以利用到并發(fā)優(yōu)勢(shì),又可以避免反復(fù)系統(tǒng)調(diào)用,還有進(jìn)程切換造成的開銷,分分鐘給你上幾千個(gè)邏輯流不費(fèi)力。這就是用戶態(tài)線程。
- 從上面可以看到,實(shí)現(xiàn)一個(gè)用戶態(tài)線程有兩個(gè)必須要處理的問題:一是碰著阻塞式I\O會(huì)導(dǎo)致整個(gè)進(jìn)程被掛起;二是由于缺乏時(shí)鐘阻塞,進(jìn)程需要自己擁有調(diào)度線程的能力。如果一種實(shí)現(xiàn)使得每個(gè)線程需要自己通過調(diào)用某個(gè)方法,主動(dòng)交出控制權(quán)。那么我們就稱這種用戶態(tài)線程是協(xié)作式的,即是協(xié)程。
而協(xié)程因?yàn)槭欠菗屨际剑孕枰脩糇约横尫攀褂脵?quán)來切換到其他協(xié)程,因此同一時(shí)間其實(shí)只有一個(gè)協(xié)程擁有運(yùn)行權(quán),相當(dāng)于單線程的能力。
阻塞I/O、非阻塞I/O I/O多路復(fù)用
https://www.cnblogs.com/skiler/p/6852493.html
下面舉一個(gè)例子,模擬一個(gè)tcp服務(wù)器處理30個(gè)客戶socket。假設(shè)你是一個(gè)老師,讓30個(gè)學(xué)生解答一道題目,然后檢查學(xué)生做的是否正確,你有下面幾個(gè)選擇:
- 第一種選擇:按順序逐個(gè)檢查,先檢查A,然后是B,之后是C、D。。。這中間如果有一個(gè)學(xué)生卡主,全班都會(huì)被耽誤。這種模式就好比,你用循環(huán)挨個(gè)處理socket,根本不具有并發(fā)能力。
- 第二種選擇:你創(chuàng)建30個(gè)分身,每個(gè)分身檢查一個(gè)學(xué)生的答案是否正確。 這種類似于為每一個(gè)用戶創(chuàng)建一個(gè)進(jìn)程或者線程處理連接。
- 第三種選擇,你站在講臺(tái)上等,誰解答完誰舉手。這時(shí)C、D舉手,表示他們解答問題完畢,你下去依次檢查C、D的答案,然后繼續(xù)回到講臺(tái)上等。此時(shí)E、A又舉手,然后去處理E和A。。。 這種就是IO復(fù)用模型,Linux下的select、poll和epoll就是干這個(gè)的。
將用戶socket對(duì)應(yīng)的fd注冊(cè)進(jìn)epoll,然后epoll幫你監(jiān)聽哪些socket上有消息到達(dá),這樣就避免了大量的無用操作。此時(shí)的socket應(yīng)該采用非阻塞模式。這樣,整個(gè)過程只在調(diào)用select、poll、epoll這些調(diào)用的時(shí)候才會(huì)阻塞,收發(fā)客戶消息是不會(huì)阻塞的,整個(gè)進(jìn)程或者線程就被充分利用起來,這就是事件驅(qū)動(dòng),所謂的reactor模式。
二、內(nèi)存管理
??程序運(yùn)行的時(shí)候,儲(chǔ)存數(shù)據(jù)和代碼的地方。斷電就沒了,比硬盤塊十倍左右。操作系統(tǒng)負(fù)責(zé)給程序分配內(nèi)存。
內(nèi)存泄露:
??如果創(chuàng)建變量后不刪除,那么變量將會(huì)越來越多,占用的內(nèi)存越來越大,最終會(huì)導(dǎo)致內(nèi)存泄露。
解決辦法:
- 定時(shí)重啟
- 自動(dòng)內(nèi)存管理(GC 垃圾回收)
語(yǔ)言內(nèi)機(jī)制會(huì)自動(dòng)釋放變量,引用次數(shù)為 0 時(shí),會(huì)被自動(dòng)釋放(java,python)。
不能自動(dòng)管理的:- 循環(huán)引用
A 引用 B,B 引用 A - 非托管資源
代碼里面的字典列表是托管資源,受語(yǔ)言控制。打開的文件,連接的數(shù)據(jù)庫(kù)是非托管資源,這些資源在不使用的時(shí)候需要被手動(dòng)釋放,語(yǔ)言不能自動(dòng)判斷你使用不使用。
- 循環(huán)引用
三、驅(qū)動(dòng)程序(硬件管理)
??每個(gè)設(shè)備(硬件)都有自己的驅(qū)動(dòng)程序,什么情況下發(fā)生了什么事情,代表什么意思,將自己的動(dòng)作轉(zhuǎn)換成操作系統(tǒng)能識(shí)別的數(shù)據(jù)。
四、文件系統(tǒng)
??文件儲(chǔ)存于磁盤上,磁盤有很多盤片,就像光盤一樣,厚是因?yàn)橛泻芏鄬印C總€(gè)盤片有很多磁,在某邊(右)會(huì)有個(gè)磁頭,通過磁頭可以讀取盤片上的磁信息。光盤是在盤片上涂化學(xué)物質(zhì),讓其變得凹凸不平,比如凹代表0,凸代表1,這樣以二進(jìn)制代表數(shù)據(jù)。磁盤靠磁來存數(shù)據(jù)。
??我們讀取數(shù)據(jù)并不關(guān)心數(shù)據(jù)存在哪,操作系統(tǒng)給我們提供了 readfile
功能,我們只需要調(diào)用這個(gè)功能,然后給這個(gè)函數(shù)提供一個(gè)路徑就可以了。操作系統(tǒng)就能去指定位置讀取信息了。這就是文件系統(tǒng)。
??會(huì)將存儲(chǔ)空間分為幾個(gè)部分,每個(gè)部分有非常多的小格子,文件系統(tǒng)會(huì)給這些小格子有意義的格式,一般分為三個(gè)部分:
- 控制信息:一般在前面,為第一部分,包括文件系統(tǒng)的版本,不同的操作系統(tǒng)版本不一樣,包含這個(gè)區(qū)域有多大,放了多少文件,還剩多少文件等。
- 索引:第二部分,儲(chǔ)存數(shù)據(jù)的地址,索引定長(zhǎng),文件不定長(zhǎng),索引指向數(shù)據(jù)的真正地址,執(zhí)行刪除操作時(shí),刪除的是索引,而非真正的數(shù)據(jù),是假刪除。加數(shù)據(jù)就是先加索引,然后在另一塊區(qū)域存儲(chǔ)真正數(shù)據(jù)。
- 數(shù)據(jù)存儲(chǔ)區(qū)域
??磁盤格式化就是將控制信息和索引刪除掉。
異步和多線程
多核出現(xiàn)后,一個(gè)cpu上面有四核,那么可以同時(shí)執(zhí)行四個(gè)任務(wù),如果 一個(gè)程序開四個(gè)進(jìn)程的話,
由于進(jìn)程相互隔離,那么就不能共享數(shù)據(jù)。那么就出現(xiàn)了線程,開四個(gè)線程,分別同時(shí)跑在四個(gè)核上,
還可以共享數(shù)據(jù)。
同步:abcd順序執(zhí)行
異步:js的回調(diào),亂序執(zhí)行,a卡了可以執(zhí)行b。和多線程沒啥關(guān)系。
1. 單線程同步和異步:
同步:順序執(zhí)行
異步:
先執(zhí)行T1,馬上返回執(zhí)行T2,然后執(zhí)行T1的callback,亂序,保證整個(gè)過程不卡死,不浪費(fèi)時(shí)間。
適合爬蟲,比如下載某個(gè)網(wǎng)頁(yè)需要一分鐘,那么下載期間可以去執(zhí)行其他任務(wù),
下載完成后再去執(zhí)行前一個(gè)任務(wù);
2. 多線程的同步和異步
多線程:有四個(gè)任務(wù),可以同時(shí)執(zhí)行
多線程同步:?jiǎn)蝹€(gè)線程執(zhí)行任務(wù)順序不變(T1-T5)
多線程異步:?jiǎn)蝹€(gè)線程里面執(zhí)行的任務(wù)亂序,但是上述3個(gè)線程可以同步執(zhí)行,代表著三個(gè)核;
多線程 + 異步 能夠充分利用 cpu 的每一個(gè)核。如果不用異步,可以開更多的線程充分利用cpu的核。
一個(gè)核可以開多個(gè)線程,劃分時(shí)間片來執(zhí)行不同線程。(大多數(shù)軟件采用方式)
gunicorn
??服務(wù)器會(huì)有多個(gè)請(qǐng)求,gunicorn 會(huì)幫我們規(guī)避 PYTHON GIL,會(huì)用多個(gè) worker 實(shí)現(xiàn)多進(jìn)程,這樣能中分利用多核。 master 會(huì)自動(dòng)分發(fā)請(qǐng)求給 worker(進(jìn)程)。
不同進(jìn)程之間相互隔離,這就導(dǎo)致了 csrf_token 不能共享,會(huì)導(dǎo)致程序錯(cuò)誤。可以通過 redies 或 存數(shù)據(jù)庫(kù)解決。
supervisor gunicorn
- supervisor:守護(hù)進(jìn)程(程序掛了自動(dòng)拉起來)
- gunicorn:管理進(jìn)程(緩解操作系統(tǒng)壓力)
- 管理進(jìn)程:master(分發(fā)任務(wù))
- 工作進(jìn)程:worker(執(zhí)行任務(wù))
Gunicorn 一般用來管理多個(gè)進(jìn)程,有進(jìn)程掛了Gunicorn可以把它拉起來,防止服務(wù)器長(zhǎng)時(shí)間停止服務(wù),還可以動(dòng)態(tài)調(diào)整 worker 的數(shù)量,請(qǐng)求多的時(shí)候增加 worker 的數(shù)量,請(qǐng)求少的時(shí)候減少,這就是所謂的 pre-fork 模型。(worker 貌似就是進(jìn)程,不是很確定,因?yàn)槲覀冊(cè)谑褂玫倪^程中沒有發(fā)現(xiàn)進(jìn)程數(shù)量有變化。。。)。
Unix/Linux操作系統(tǒng)提供了一個(gè)fork()系統(tǒng)調(diào)用,它非常特殊。普通的函數(shù)調(diào)用,調(diào)用一次,返回一次,但是fork()調(diào)用一次,返回兩次,因?yàn)椴僮飨到y(tǒng)自動(dò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。這樣做的理由是,一個(gè)父進(jìn)程可以fork出很多子進(jìn)程,所以,父進(jìn)程要記下每個(gè)子進(jìn)程的ID,而子進(jìn)程只需要調(diào)用getppid()就可以拿到父進(jìn)程的ID。
多線程實(shí)際上是多個(gè)線程之間輪流執(zhí)行的,就是將一個(gè)時(shí)間段分成若干個(gè)時(shí)間片,每個(gè)線程只運(yùn)行一個(gè)時(shí)間片,由于時(shí)間片極短,而且電腦運(yùn)行極快,線程之間切換也極快,幾乎可以看做是并行運(yùn)行的,也就是說可以看成是同時(shí)運(yùn)行的。但實(shí)際卻不是同時(shí)運(yùn)行。這是假多線程,python就是這樣。真多線程是線程s可以同時(shí)執(zhí)行。