參考:https://blog.csdn.net/qq_27825451/article/details/86162597
聲明:后面會不斷穿插這樣的一些概念,一定要深入理解一些關鍵的基本思想。這些基本概念很多的參考資料參差不齊,講解不是很清楚,本章將詳細,用最通俗易懂的語言解釋,什么是線程、進程、同步、異步、阻塞、非阻塞、並發、並行這些很容易弄混的概念,本次的系列文章較長,后續會講解python協程的實現方式。看完本文,你講明白一下一些基本的東西:
(1)並發(並發只是實現異步的手段之一)並不是沒有阻塞的,依然有阻塞。
(2)怎么理解“事件循環”,那個線程一直在各個方法直接不停歇的游走,遇到一個yield from就懸掛起來,然后又走到另外一個方法,依次進行下去,直到事件循環所有的方法執行完畢。
(3)並發(異步)一定比同步快嗎?當然不是了
(4)並發分為真並發,偽並發,並發於並行的區別在於“是否同時”
(5)異步是最終的目的,並發和並行都可以實現異步,線程是決定使用並發還是並行的手段。
(6)最好的實現方式當然是並行了。
首先介紹一些最基本的概念和核心思想
一,進程,線程
1.1進程(Process)
是Windows系統中的一個基本概念,它包含着一個運行程序所需要的資源。一個正在運行的應用程序在操作系統中被視為一個進程,進程可以包括一個或多個線程。線程是操作系統分配處理器時間的基本單元,在進程中可以有多個線程同時執行代碼。進程之間是相對獨立的,一個進程無法訪問另一個進程的數據(除非利用分布式計算方式),一個進程運行的失敗也不會影響其他進程的運行,Windows系統就是利用進程把工作划分為多個獨立的區域的。進程可以理解為一個程序的基本邊界。是應用程序的一個運行例程,是應用程序的一次動態執行過程。
1.2線程(Thread)
是進程中的基本執行單元,是操作系統分配CPU時間的基本單位,一個進程可以包含若干個線程,在進程入口執行的第一個線程被視為這個進程的主線程。在.NET應用程序中,都是以Main()方法作為入口的,當調用此方法時系統就會自動創建一個主線程。線程主要是由CPU寄存器、調用棧和線程本地存儲器(Thread Local Storage,TLS)組成的。CPU寄存器主要記錄當前所執行線程的狀態,調用棧主要用於維護線程所調用到的內存與數據,TLS主要用於存放線程的狀態信息。
線程的本質:線程不是一個計算機硬件的功能,而是操作系統提供的一種邏輯功能,線程本質上是進程中一段並發運行的代碼,所以線程需要操作系統投入CPU資源來運行和調度。
1.3進程和線程的區別
進程和線程的主要差別在於它們是不同的操作系統資源管理方式。進程有獨立的地址空間,一個進程崩潰后,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不同執行路徑。線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對於一些要求同時進行並且又要共享某些變量的並發操作,只能用線程,不能用進程。
1) 簡而言之,一個程序至少有一個進程,一個進程至少有一個線程.
2) 線程的划分尺度小於進程,使得多線程程序的並發性高。
3) 另外,進程在執行過程中擁有獨立的內存單元,而多個線程共享內存,從而極大地提高了程序的運行效率。
4) 線程在執行過程中與進程還是有區別的。每個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口。但是線程不能夠獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。
5) 從邏輯角度來看,多線程的意義在於一個應用程序中,有多個執行部分可以同時執行。但操作系統並沒有將多個線程看做多個獨立的應用,來實現進程的調度和管理以及資源分配。這就是進程和線程的重要區別。
二,同步(Sync)和異步(Async)
2.1 同步
所謂同步,就是發出一個功能調用時,在沒有得到結果之前,該調用就不放或繼續執行后續操作。
簡單來說,同步就是必須一件一件事做,等前一件作為了才能做下一件事。
2.2異步
異步於同步相對,當一個異步過程調用發出后,調用者在沒有得到結果之前,就可以繼續執行后續操作。當這個調用完成后,一般通過狀態,通知和回調來通知調用者。對於異步調用,調用的返回並不受調用者控制。
對於通知調用者的三種方式,具體如下
狀態
即監聽被調用者的狀態(輪詢),調用者沒隔一段時間檢查一次,效率會很低。
通知
當被調用者執行完成后,發出通知告知調用者,無需消耗太多性能。
回調
與通知類似,當被調用者執行完成后,會調用調用者提供的回調函數。
2.3 同步和異步的區別
總結來說,同步和異步的區別:請求發出后,是否需要等待結果,才能繼續執行其他操作。
三,阻塞和非阻塞
阻塞和非阻塞這兩個概率僅僅與等待消息通知時的狀態有關。跟同步,異步沒什么太大關系,也就是說阻塞與非阻塞主要是程序(線程)等待消息通知時的狀態角度來說的。
阻塞和非阻塞關注的是程序在等待調用結果(消息,返回值)時的狀態.
阻塞調用是指調用結果返回之前,當前線程會被掛起。調用線程只有在得到結果之后才會返回。
非阻塞調用指在不能立刻得到結果之前,該調用不會阻塞當前線程。
總結:同步執行一般都會有阻塞,但也有可能沒阻塞;異步執行也有可能有阻塞,也可能沒有阻塞。后面會講到。
四,並發並行
並發:在操作系統中,是指一個時間段中有幾個程序都處於已啟動運行到運行完畢之間,且這幾個程序都是在同一個處理機上運行,但任意一個時刻點上只有一個程序在處理機上運行。當有多個線程在操作時,如果系統只有一個CPU,則它根本不可能真正同時進行一個以上的線程,它只能把CPU運行時間划分成若干個時間段,再將時間段分配給各個線程執行,在一個時間段的線程代碼運行時,其他線程處於掛起狀態。這種方式我們稱之為並發(Concurrent)。
並行:當系統有一個以上CPU時,則線程的操作有可能非並發。當一個CPU執行一個線程時,另一個CPU執行另一個線程,兩個線程互不搶占CPU資源,可以同時進行,這種方式我們稱之為並行(Parallel)
並發與並行的區別:
並發和並行的區別:
(1)你吃飯吃到一半,電話來了,你一直到吃完了以后才去接,這就說明你不支持並發也不支持並行。因為在完成吃飯這件事情之前,打電話這件事你是完全沒開始的,是一個一個來的。
(2)你吃飯吃到一半,電話來了,你停了下來接了電話,接完后繼續吃飯,這說明你支持並發。因為吃飯和電話兩件事情都處於啟動狀態,而不是一件事做完才啟動另一件事,但是雖然幾件事情都開始了,但因為是一個線程,還是一個一個交替去做的,這也是python協程的思想。
(3)你吃飯吃到一半,電話來了,你一邊打電話一邊吃飯,這說明你支持並行。因為這是同時在進行多件事情,而不是交替執行。
怎么區別呢?區分它們最關鍵的點就是:是否是『同時』。
並發的關鍵是你有處理多個任務的能力,不一定要同時;但是並行的關鍵是你有同時處理多個任務的能力。
五,關鍵概念的區分
(1)阻塞/非阻塞:關注的是程序在等待調用結果(消息,返回值)時的狀態
(2)同步/異步:關注的是消息通知的機制。即等到完全做完才通知,還是你先做你的,我先做我的 ,你做完了再來通知我就可以了。
所謂同步,就是在發出一個*調用*時,在沒有得到結果之前,該*調用*就不返回。但是一旦調用返回,就得到返回值了。
換句話說,就是由*調用者*主動等待這個*調用*的結果。
而異步則是相反,*調用*在發出之后,這個調用就直接返回了,所以沒有返回結果。換句話說,當一個異步過程調用發出后,調用者不會立刻得到結果。而是在*調用*發出后,*被調用者*通過狀態、通知來通知調用者,或通過回調函數處理這個調用。
上面的兩組概念是可以兩兩搭配的,即
(3)同步阻塞、同步非阻塞,異步阻塞、異步非阻塞。
舉個簡單的例子來描述這四種情況,老張要做兩件事,用水壺燒開水,看電視,兩件事情即兩個任務,兩個函數。
同步阻塞:老張把水壺放到火上,就坐在那里等水開,開了之后我再去看電視。()
同步非阻塞:老張把水壺放到火上,去客廳看電視,時不時去廚房看看水開沒有。(同步非阻塞)
老張還是覺得自己有點傻,於是變高端了,買了把會響笛的那種水壺。水開之后,能大聲發出嘀~~~~的噪音。
異步阻塞:老張把響水壺放到火上,然后就坐在旁邊等着聽那個燒開的提示音。(異步阻塞)
異步非阻塞:老張把響水壺放到火上,去客廳看電視,水壺響之前不再去看它了,響了再去拿壺。(異步非阻塞)
乍一看,這“同步阻塞、異步阻塞”似乎沒有什么區別,但實際上是有區別的,所謂同步異步,指的是消息通知的機制。區別在哪里呢?
在這個例子中同步異步只是對於水壺而言。在使用普通水壺的時候,我要自己主動去觀察水是不是燒開了,自己主動去獲取燒開的這個結果,即所謂的同步;但是在響水壺的時候,我不需要再管水燒到什么程度了,因為只要水燒開了,那個滴滴的噪聲就會通知我的,即所謂的異步。
他們的相同點是,在燒水的過程中,老王啥也沒干,即“阻塞”。
(4)四種總結----同步/異步與阻塞/非阻塞
同步阻塞形式:效率是最低的。拿上面的例子來說,在燒水的過程中,什么別的事都不做。
同步非阻塞形式:實際上是效率低下的。因為老王需要不斷的在看電視與燒水之間來回跑動,看一下電視,又要去看一下水燒開 沒有,這樣來回跑很多次,在程序中,程序需要在這兩種不同的行為之間來回的切換,效率可想而知是低下的。
異步阻塞形式:異步操作是可以被阻塞住的,只不過它不是在處理消息時阻塞,而是在等待消息通知時被阻塞。
這個效率其實跟同步阻塞差不多的。
異步非阻塞形式:效率更高。因為老王把水燒好之后就不用管了,可以安安心心去看電視,不用來回奔波看水燒開了沒,因為水燒開了會有提示告訴他水燒好了,這樣效率豈不是更高。
那有沒有更好的辦法?當然有,如果老王還有一個幫手老張,讓老王自己看電視、同時老張去燒開水,這樣豈不是更好?這就是所謂的並行。
(5)並發/並行、同步/異步、阻塞/非阻塞
並發/並行:即能夠開啟多個任務,多個任務交替執行為並發,多個任務同時執行為並行
同步/異步:關注的是消息通知的機制,主動等候消息則為同步、被動聽消息則為異步
阻塞/非阻塞:關注的是等候消息的過程中有沒有干其他事。
總結:上面的幾組概念,時刻穿插的,並沒有完全的等價關系,所以經常有人說,異步就是非阻塞,同步就是阻塞,並發就是非阻塞、並行就是非阻塞,這些說法都是不完全准確地。
六、最終結論概括
並發和並行都是實現異步編程的思路,只有一個線程的並發,稱之為“偽並發”;有多個線程的並發稱之為“真並發”,真並發與並行是很接近的。
6.1異步操作的優缺點
因為異步操作無須額外的線程負擔(這里指的是單線程交替執行的“偽並發”),並且使用回調的方式進行處理,在設計良好的情況下,處理函數可以不必使用共享變量(即使無法完全不用,最起碼可以減少 共享變量的數量),減少了死鎖的可能。當然異步操作也並非完美無暇。編寫異步操作的復雜程度較高,程序主要使用回調方式進行處理,與普通人的思維方式有些出入,而且難以調試。
6.2 多線程的優缺點
多線程的優點很明顯,線程中的處理程序依然是順序執行,符合普通人的思維習慣,所以編程簡單。但是多線程的缺點也同樣明顯,線程的使用(濫用)會給系統帶來上下文切換的額外負擔。並且線程間的共享變量可能造成死鎖的出現。
異步與多線程,從辯證關系上來看,異步和多線程並不時一個同等關系,(因為單線程也是可以實現異步的)異步是目的,多線程只是我們實現異步的一個手段.什么是異步:異步是當一個調用請求發送給被調用者,而調用者不用等待其結果的返回.實現異步可以采用多線程技術或則交給另外的進程來處理
