同步与异步以及线程与进程
生活随笔
收集整理的這篇文章主要介紹了
同步与异步以及线程与进程
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
寫過爬蟲的都知道,爬蟲的性能瓶頸在于IO,因為爬蟲是一個IO密集型業(yè)務(wù),程序需要發(fā)起網(wǎng)絡(luò)請求,必然就有IO阻塞,
通常請求一個URL耗時要幾百毫秒到幾秒不等,而我們的CPU處理速度驚人,兩者的速度就好比烏龜跟火箭的差別。
在單線程同步阻塞程序中,如果要獲取一個網(wǎng)站所有的URL,假設(shè)100個URL,平均每個URL請求的時間是1秒,
那么在單線程同步場景下,最快也需要100秒鐘,才能把所有的頁面爬取下來。
于是,我們想到了更好的一種辦法就是采用多線程或者多進程,但是由于在Python中臭名昭著的GIL,導致做不到真正的并行運算,
在同一時間內(nèi),就算有多核CPU,也無法被利用起來,這樣雖然能做到并發(fā),但是沒法并行,在單個CPU里切換線程,還有切換成本,以及線程的創(chuàng)造成本。
如果使用多進程,雖然能利用多核處理的優(yōu)勢,但是多進程的創(chuàng)建成本比線程更高,而IO密集型任務(wù),CPU不是瓶頸。
所以,后來Python引入了異步編程,異步編程使得CPU不再需要再去等待耗時的操作,而是讓出CPU時間給其他任務(wù)執(zhí)行,
熟悉電腦硬件的朋友肯定對DMA這個詞不陌生,硬盤、光驅(qū)的技術(shù)規(guī)格中都有明確DMA的模式指標,其實網(wǎng)卡、聲卡、顯卡也是有DMA功能的。
DMA就是直接內(nèi)存訪問的意思,也就是說,擁有DMA功能的硬件在和內(nèi)存進行數(shù)據(jù)交換的時候可以不消耗CPU資源。
只要CPU在發(fā)起數(shù)據(jù)傳輸時發(fā)送一個指令,硬件就開始自己和內(nèi)存交換數(shù)據(jù),在傳輸完成之后硬件會觸發(fā)一個中斷來通知操作完成。
這些無須消耗CPU時間的I/O操作正是異步操作的硬件基礎(chǔ)。所以即使在DOS這樣的單進程(而且無線程概念)系統(tǒng)中也同樣可以發(fā)起異步的DMA操作。
而異步則是相反,*調(diào)用*在發(fā)出之后,這個調(diào)用就直接返回了,所以沒有返回結(jié)果。換句話說,當一個異步過程調(diào)用發(fā)出后,調(diào)用者不會立刻得到結(jié)果。
而是在*調(diào)用*發(fā)出后,*被調(diào)用者*通過狀態(tài)、通知來通知調(diào)用者,或通過回調(diào)函數(shù)處理這個調(diào)用。典型的異步編程模型比如Node.js
舉個通俗的例子:
你打電話問書店老板有沒有《分布式系統(tǒng)》這本書,如果是同步通信機制,書店老板會說,你稍等,”我查一下",然后開始查啊查,等查好了(可能是5秒,也可能是一天)告訴你結(jié)果(返回結(jié)果)。
而異步通信機制,書店老板直接告訴你我查一下啊,查好了打電話給你,然后直接掛電話了(不返回結(jié)果)。然后查好了,他會主動打電話給你。在這里老板通過“回電”這種方式來回調(diào)。
阻塞調(diào)用是指調(diào)用結(jié)果返回之前,當前線程會被掛起。調(diào)用線程只有在得到結(jié)果之后才會返回。
非阻塞調(diào)用指在不能立刻得到結(jié)果之前,該調(diào)用不會阻塞當前線程。
還是上面的例子,
你打電話問書店老板有沒有《分布式系統(tǒng)》這本書,你如果是阻塞式調(diào)用,你會一直把自己“掛起”,直到得到這本書有沒有的結(jié)果,
如果是非阻塞式調(diào)用,你不管老板有沒有告訴你,你自己先一邊去玩了, 當然你也要偶爾過幾分鐘check一下老板有沒有返回結(jié)果。
在這里阻塞與非阻塞與是否同步異步無關(guān)。跟老板通過什么方式回答你結(jié)果無關(guān)。
進程:具有一定獨立功能的程序關(guān)于某個數(shù)據(jù)集合上的一次運行活動,進程是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位.
線程:進程的一個實體,是CPU調(diào)度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。
線程自己基本上不擁有系統(tǒng)資源,只擁有一點在運行中必不可少的資源(如程序計數(shù)器,一組寄存器和棧),
但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源.
2.關(guān)系
一個線程可以創(chuàng)建和撤銷另一個線程;同一個進程中的多個線程之間可以并發(fā)執(zhí)行.
相對進程而言,線程是一個更加接近于執(zhí)行體的概念,它可以與同進程中的其他線程共享數(shù)據(jù),但擁有自己的棧空間,擁有獨立的執(zhí)行序列。
3.區(qū)別
進程和線程的主要差別在于它們是不同的操作系統(tǒng)資源管理方式。進程有獨立的地址空間,一個進程崩潰后,在保護模式下不會對其它進程產(chǎn)生影響。
而線程只是一個進程中的不同執(zhí)行路徑。線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等于整個進程死掉,
所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。
但對于一些要求同時進行并且又要共享某些變量的并發(fā)操作,只能用線程,不能用進程。
1) 簡而言之,一個程序至少有一個進程,一個進程至少有一個線程.
2) 線程的劃分尺度小于進程,使得多線程程序的并發(fā)性高。
3) 另外,進程在執(zhí)行過程中擁有獨立的內(nèi)存單元,而多個線程共享內(nèi)存,從而極大地提高了程序的運行效率。
4) 線程在執(zhí)行過程中與進程還是有區(qū)別的。每個獨立的線程有一個程序運行的入口、順序執(zhí)行序列和程序的出口。
? ?但是線程不能夠獨立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個線程執(zhí)行控制。
5) 從邏輯角度來看,多線程的意義在于一個應(yīng)用程序中,有多個執(zhí)行部分可以同時執(zhí)行。
? ?但操作系統(tǒng)并沒有將多個線程看做多個獨立的應(yīng)用,來實現(xiàn)進程的調(diào)度和管理以及資源分配。這就是進程和線程的重要區(qū)別。
4.優(yōu)缺點
線程和進程在使用上各有優(yōu)缺點:
線程執(zhí)行開銷小,但不利于資源的管理和保護;而進程正相反。
Why:?
由于CPU與其他PC資源之間速度的不協(xié)調(diào),人們想提高資源利用率,所以人們提出了多任務(wù)系統(tǒng)。
得益于CPU的計算速度,我們可以“同時”運行多個任務(wù),實質(zhì)上是多個任務(wù)之間輪流使用CPU資源,由于速度超快,給用戶的感覺就是連續(xù)的。
How:?
1)任務(wù)的執(zhí)行需要依賴各個PC資源,我們可以稱為計算機執(zhí)行的上下文環(huán)境。
? ?要實現(xiàn)“同時執(zhí)行”,就需要不斷輪換,為了后來繼續(xù)從當前狀態(tài)執(zhí)行下去,計算機需要保存切換前的程序上下文。
? ?所以有了進程:用進程去描述程序當前上下文的狀態(tài)信息----內(nèi)存位置、變量值、任務(wù)ID……所以,進程是資源分配的單位。
? ?一般來說宏觀上可以看做是一個軟件的運行,例如一個word文檔的打開。
2)多個任務(wù)之間切換因為要保存上下文、調(diào)入上下文,一旦多了的時候,還是有一定的時間消耗的。
? ?為了進一步提高資源利用率,人們在進程中,引入了線程,線程只是CPU輪流調(diào)度的單位,其他上下文信息用所在進程中的。
? ?這樣上下文切換的耗時就降了下來。同樣的,宏觀上來可以看做是一個軟件中的多個處理功能,例如上述打開word中拼寫檢查功能、字體加粗……
So, What:?
一般來說,進程是資源的分配單位,線程是CPU在進程內(nèi)切換的單位,線程屬于進程。所以運行某個軟件,相當于開了一個進程。
在這個軟件運行的過程里(在這個進程里),多個工作支撐的完成QQ的運行,那么這“多個工作”分別有一個線程。
再打一個通俗易懂的比方吧!
比如你去一個食堂吃飯。里面有A,B,C,D等一些窗口可以打飯菜或者米粉麻辣燙什么的。
但是每一個窗口又有打這些菜的師傅。
那么這些窗口就是進程。
那個那些窗口里面打菜的師傅就是線程。
這個食堂就是系統(tǒng)了,系統(tǒng)去分配這些進程。 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎
通常請求一個URL耗時要幾百毫秒到幾秒不等,而我們的CPU處理速度驚人,兩者的速度就好比烏龜跟火箭的差別。
在單線程同步阻塞程序中,如果要獲取一個網(wǎng)站所有的URL,假設(shè)100個URL,平均每個URL請求的時間是1秒,
那么在單線程同步場景下,最快也需要100秒鐘,才能把所有的頁面爬取下來。
于是,我們想到了更好的一種辦法就是采用多線程或者多進程,但是由于在Python中臭名昭著的GIL,導致做不到真正的并行運算,
在同一時間內(nèi),就算有多核CPU,也無法被利用起來,這樣雖然能做到并發(fā),但是沒法并行,在單個CPU里切換線程,還有切換成本,以及線程的創(chuàng)造成本。
如果使用多進程,雖然能利用多核處理的優(yōu)勢,但是多進程的創(chuàng)建成本比線程更高,而IO密集型任務(wù),CPU不是瓶頸。
所以,后來Python引入了異步編程,異步編程使得CPU不再需要再去等待耗時的操作,而是讓出CPU時間給其他任務(wù)執(zhí)行,
這樣假如有100個任務(wù),每個任務(wù)是1秒鐘,就可以做到理論上只需要1秒鐘就可以完成所有的任務(wù)。
同步與異步
異步操作的本質(zhì)
所有的程序最終都會由計算機硬件來執(zhí)行,所以為了更好的理解異步操作的本質(zhì),我們有必要了解一下它的硬件基礎(chǔ)。?熟悉電腦硬件的朋友肯定對DMA這個詞不陌生,硬盤、光驅(qū)的技術(shù)規(guī)格中都有明確DMA的模式指標,其實網(wǎng)卡、聲卡、顯卡也是有DMA功能的。
DMA就是直接內(nèi)存訪問的意思,也就是說,擁有DMA功能的硬件在和內(nèi)存進行數(shù)據(jù)交換的時候可以不消耗CPU資源。
只要CPU在發(fā)起數(shù)據(jù)傳輸時發(fā)送一個指令,硬件就開始自己和內(nèi)存交換數(shù)據(jù),在傳輸完成之后硬件會觸發(fā)一個中斷來通知操作完成。
這些無須消耗CPU時間的I/O操作正是異步操作的硬件基礎(chǔ)。所以即使在DOS這樣的單進程(而且無線程概念)系統(tǒng)中也同樣可以發(fā)起異步的DMA操作。
同步和異步關(guān)注的是消息通信機制 (synchronous communication/ asynchronous communication)
所謂同步,就是在發(fā)出一個*調(diào)用*時,在沒有得到結(jié)果之前,該*調(diào)用*就不返回。但是一旦調(diào)用返回,就得到返回值了。換句話說,就是由*調(diào)用者*主動等待這個*調(diào)用*的結(jié)果。而異步則是相反,*調(diào)用*在發(fā)出之后,這個調(diào)用就直接返回了,所以沒有返回結(jié)果。換句話說,當一個異步過程調(diào)用發(fā)出后,調(diào)用者不會立刻得到結(jié)果。
而是在*調(diào)用*發(fā)出后,*被調(diào)用者*通過狀態(tài)、通知來通知調(diào)用者,或通過回調(diào)函數(shù)處理這個調(diào)用。典型的異步編程模型比如Node.js
舉個通俗的例子:
你打電話問書店老板有沒有《分布式系統(tǒng)》這本書,如果是同步通信機制,書店老板會說,你稍等,”我查一下",然后開始查啊查,等查好了(可能是5秒,也可能是一天)告訴你結(jié)果(返回結(jié)果)。
而異步通信機制,書店老板直接告訴你我查一下啊,查好了打電話給你,然后直接掛電話了(不返回結(jié)果)。然后查好了,他會主動打電話給你。在這里老板通過“回電”這種方式來回調(diào)。
阻塞與非阻塞
阻塞和非阻塞關(guān)注的是程序在等待調(diào)用結(jié)果(消息,返回值)時的狀態(tài)。阻塞調(diào)用是指調(diào)用結(jié)果返回之前,當前線程會被掛起。調(diào)用線程只有在得到結(jié)果之后才會返回。
非阻塞調(diào)用指在不能立刻得到結(jié)果之前,該調(diào)用不會阻塞當前線程。
還是上面的例子,
你打電話問書店老板有沒有《分布式系統(tǒng)》這本書,你如果是阻塞式調(diào)用,你會一直把自己“掛起”,直到得到這本書有沒有的結(jié)果,
如果是非阻塞式調(diào)用,你不管老板有沒有告訴你,你自己先一邊去玩了, 當然你也要偶爾過幾分鐘check一下老板有沒有返回結(jié)果。
在這里阻塞與非阻塞與是否同步異步無關(guān)。跟老板通過什么方式回答你結(jié)果無關(guān)。
進程和線程的區(qū)別
1.定義進程:具有一定獨立功能的程序關(guān)于某個數(shù)據(jù)集合上的一次運行活動,進程是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位.
線程:進程的一個實體,是CPU調(diào)度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。
線程自己基本上不擁有系統(tǒng)資源,只擁有一點在運行中必不可少的資源(如程序計數(shù)器,一組寄存器和棧),
但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源.
2.關(guān)系
一個線程可以創(chuàng)建和撤銷另一個線程;同一個進程中的多個線程之間可以并發(fā)執(zhí)行.
相對進程而言,線程是一個更加接近于執(zhí)行體的概念,它可以與同進程中的其他線程共享數(shù)據(jù),但擁有自己的棧空間,擁有獨立的執(zhí)行序列。
3.區(qū)別
進程和線程的主要差別在于它們是不同的操作系統(tǒng)資源管理方式。進程有獨立的地址空間,一個進程崩潰后,在保護模式下不會對其它進程產(chǎn)生影響。
而線程只是一個進程中的不同執(zhí)行路徑。線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等于整個進程死掉,
所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。
但對于一些要求同時進行并且又要共享某些變量的并發(fā)操作,只能用線程,不能用進程。
1) 簡而言之,一個程序至少有一個進程,一個進程至少有一個線程.
2) 線程的劃分尺度小于進程,使得多線程程序的并發(fā)性高。
3) 另外,進程在執(zhí)行過程中擁有獨立的內(nèi)存單元,而多個線程共享內(nèi)存,從而極大地提高了程序的運行效率。
4) 線程在執(zhí)行過程中與進程還是有區(qū)別的。每個獨立的線程有一個程序運行的入口、順序執(zhí)行序列和程序的出口。
? ?但是線程不能夠獨立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個線程執(zhí)行控制。
5) 從邏輯角度來看,多線程的意義在于一個應(yīng)用程序中,有多個執(zhí)行部分可以同時執(zhí)行。
? ?但操作系統(tǒng)并沒有將多個線程看做多個獨立的應(yīng)用,來實現(xiàn)進程的調(diào)度和管理以及資源分配。這就是進程和線程的重要區(qū)別。
4.優(yōu)缺點
線程和進程在使用上各有優(yōu)缺點:
線程執(zhí)行開銷小,但不利于資源的管理和保護;而進程正相反。
舉例
開個QQ,開了一個進程;開了迅雷,開了一個進程。在QQ的這個進程里,傳輸文字開一個線程、傳輸語音開了一個線程、彈出對話框又開了一個線程。Why:?
由于CPU與其他PC資源之間速度的不協(xié)調(diào),人們想提高資源利用率,所以人們提出了多任務(wù)系統(tǒng)。
得益于CPU的計算速度,我們可以“同時”運行多個任務(wù),實質(zhì)上是多個任務(wù)之間輪流使用CPU資源,由于速度超快,給用戶的感覺就是連續(xù)的。
How:?
1)任務(wù)的執(zhí)行需要依賴各個PC資源,我們可以稱為計算機執(zhí)行的上下文環(huán)境。
? ?要實現(xiàn)“同時執(zhí)行”,就需要不斷輪換,為了后來繼續(xù)從當前狀態(tài)執(zhí)行下去,計算機需要保存切換前的程序上下文。
? ?所以有了進程:用進程去描述程序當前上下文的狀態(tài)信息----內(nèi)存位置、變量值、任務(wù)ID……所以,進程是資源分配的單位。
? ?一般來說宏觀上可以看做是一個軟件的運行,例如一個word文檔的打開。
2)多個任務(wù)之間切換因為要保存上下文、調(diào)入上下文,一旦多了的時候,還是有一定的時間消耗的。
? ?為了進一步提高資源利用率,人們在進程中,引入了線程,線程只是CPU輪流調(diào)度的單位,其他上下文信息用所在進程中的。
? ?這樣上下文切換的耗時就降了下來。同樣的,宏觀上來可以看做是一個軟件中的多個處理功能,例如上述打開word中拼寫檢查功能、字體加粗……
So, What:?
一般來說,進程是資源的分配單位,線程是CPU在進程內(nèi)切換的單位,線程屬于進程。所以運行某個軟件,相當于開了一個進程。
在這個軟件運行的過程里(在這個進程里),多個工作支撐的完成QQ的運行,那么這“多個工作”分別有一個線程。
再打一個通俗易懂的比方吧!
比如你去一個食堂吃飯。里面有A,B,C,D等一些窗口可以打飯菜或者米粉麻辣燙什么的。
但是每一個窗口又有打這些菜的師傅。
那么這些窗口就是進程。
那個那些窗口里面打菜的師傅就是線程。
這個食堂就是系統(tǒng)了,系統(tǒng)去分配這些進程。 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎
總結(jié)
以上是生活随笔為你收集整理的同步与异步以及线程与进程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Jmeter BeanShell取样器操
- 下一篇: Pytest之pytest-assume