Python 升級之路 ( Lv28 ) 并發(fā)編程初識
今天我們將學習并發(fā)編程涉及的幾個概念,包括CPU執(zhí)行任務的三種方式:串行、并行、并發(fā);程序的三種存在形式:進程、線程、協(xié)程以及相關衍生概念;再加上消息通信的兩種方式: 同步、異步。
今日冒險片段上:
傳說暗黑城的盡頭每次都會自動改變路徑,許多前輩進入這里再也沒有出來,暗影迷宮因此得名。了不起從蜘蛛洞穴深入到迷宮后,便發(fā)覺這里到處充滿了陰森和恐懼。由于長期生活在陰暗潮濕的環(huán)境中,精靈們發(fā)生了變異,這些類似于人形的上位精靈不但各種屬性增強了許多,就連智商也在逐漸與人類接近。??
眼看著就要突破迷宮的出口,三道黑影突然閃現(xiàn)在勇士的面前,嘴里還嘟囔著:“就憑你們也想活著走出這里?先過了我這關再說吧。”說話的這位就是暗影迷宮的領主——影子劍士剎影,邊上兩位毫無疑問就是這里的將領了。身為阿拉德大陸的冒險家們自然不會因為這句話就膽怯,一場戰(zhàn)斗一觸即發(fā),俗話說的好“明槍易躲,暗箭難防!”這個強悍的影子劍士居然會鬼劍士的所有技能,什么鬼影步、鬼斬、冰霜薩亞,十分棘手,更可恨是,那兩位將領狩魂者和莫比一個不停的在遠處釋放回旋鏢,一個在暗處安放地雷,著實令人頭疼。
一、串行 & 并行 & 并發(fā)
1. 串行
一句話概: 一個CPU上,按順序完成多個任務
串行圖解:
2. 并發(fā)
一句話概括: 一個cpu上, 交替執(zhí)行多個任務
并發(fā)圖解:
3. 并行
一句話概括: 多個cpu上, 同時執(zhí)行多個任務(任務數(shù)<=CPU核數(shù))并行必須有多cpu才能實現(xiàn),否則只能實現(xiàn)并發(fā)(偽并行)
并行圖解:
串行, 并行, 并發(fā)總結如下:
- 串行: 一個CPU上,按順序完成多個任務
- 并行: 多個cpu上, 同時執(zhí)行多個任務(任務數(shù)<=CPU核數(shù))
- 并發(fā): 一個cpu上, 交替執(zhí)行多個任務
二、進程 & 線程 & 協(xié)程
1. 進程
進程是資源(CPU、內存等)分配的基本單位,它是程序執(zhí)行時的一個實例.程序運行時系統(tǒng)就會創(chuàng)建一個進程,并為它分配資源,然后把該進程放入進程就緒隊列,進程調度器選中它的時候就會為它分配CPU時間,程序開始真正運行.
進程的優(yōu)點:
- 可以使用計算機多核,進行任務的并行執(zhí)行,提高執(zhí)行效率
- 運行不受其他進程影響,創(chuàng)建方便
- 空間獨立,數(shù)據(jù)安全
進程的缺點:
- 進程的創(chuàng)建和刪除消耗的系統(tǒng)資源較多
2. 線程
線程是程序執(zhí)行時的最小單位,也是CPU調度和分派的基本單位.一個進程可以由很多個線程組成,擁有自己獨立的棧和共享的堆,共享堆,不共享棧,標準線程由操作系統(tǒng)調度. 線程由CPU獨立調度執(zhí)行,在多CPU環(huán)境下就允許多個線程同時運行. 同樣多線程也可以實現(xiàn)并發(fā)操作,每個請求分配一個線程來處理.
多線程多線程可以理解為在同一個程序中能夠同時運行多個不同的線程來執(zhí)行不同的任務,這些線程可以同時利用CPU的多個核心運行. 多線程編程能夠最大限度的利用CPU的資源: 如果某些線程不需要占用CPU時間片時, 可以讓出當前時間片, 讓其他線程獲取到CPU資源. 以此來達到最大限度利用CPU資源的目的. 這個過程也被成為上下文切換。
線程生命周期線程主要有“**新建”(NEW)、“就緒”(RUNNABLE)、“運行”(RUNNING)、“阻塞”(BLOCKED)、“死亡”(DEAD)**** 五種狀態(tài). 各狀態(tài)間的轉換如下圖所示:
注意事項:
- 在運行過程中,線程由就緒態(tài)(RUNNABLE )轉為非就緒態(tài)(BLOCKED )的過程就是線程上下文切換
- 線程的狀態(tài)由運行轉為阻塞 ,再由阻塞轉為就緒 ,然后再被調度器選中執(zhí)行,這就是一個上下文切換的過程
- 當一個線程從運行狀態(tài)轉為阻塞狀態(tài)時,我們稱為一個線程的暫停,線程暫停被切出之后,操作系統(tǒng)會保存相應的上下文,以便這個線程稍后再次進入就緒狀態(tài)時能夠在之前執(zhí)行進度的基礎上繼續(xù)執(zhí)行
- 當一個線程從阻塞狀態(tài)進入到就緒狀態(tài)時,我們稱為一個線程的喚醒,此時線程將獲取上次保存的上下文繼續(xù)完成執(zhí)行
- 頻繁的上下文切換會帶來系統(tǒng)開銷, 因此會導致系統(tǒng)性能下降. 所以我們在使用多線程是應該盡量避免出現(xiàn)上下文切換.
3. 協(xié)程
協(xié)程也叫作纖程(Fiber),是一種在線程中,比線程更加輕量級的存在,由程序員自己寫程序來管理.我們可以將協(xié)程理解為運行在線程上的代碼塊, 協(xié)程掛起并不會引起線程阻塞, 他的作用是提高線程的利用率.. 協(xié)程之間可以依靠郵箱來進行通信和數(shù)據(jù)共享, 了避免內存共享數(shù)據(jù)而帶來的線程安全問題. 因為其輕量和高利用率的特點, 即使創(chuàng)建上千個線程也不會對系統(tǒng)造成很大負擔, 而線程則恰恰相反. 協(xié)程是一種設計思想,不僅僅局限于某一門語言. 在Go, Java, Python 等語言中均有實現(xiàn)。
協(xié)程的核心(控制流的讓出和恢復):
- 每個協(xié)程有自己的執(zhí)行棧,可以保存自己的執(zhí)行現(xiàn)場
- 可以由用戶程序按需創(chuàng)建協(xié)程(比如:遇到io操作)
- 協(xié)程“主動讓出(yield)”執(zhí)行權時候,會保存執(zhí)行現(xiàn)場(保存中斷時的寄存器上下文和棧),然后切換到其他協(xié)程
- 協(xié)程恢復執(zhí)行(resume)時,根據(jù)之前保存的執(zhí)行現(xiàn)場恢復到中斷前的狀態(tài),繼續(xù)執(zhí)行,這樣就通過協(xié)程實現(xiàn)了輕量的由用戶態(tài)調度的多任務模
進程和線程之間的區(qū)別:
- 每個進程都有獨立的代碼和數(shù)據(jù)空間(進程上下文),進程間的切換會有較大的開銷.
- 線程可以看成是輕量級的進程,屬于同一進程的線程共享代碼和數(shù)據(jù)空間,每個線程有獨立的運行棧和程序計數(shù)器(PC),線程切換的開銷小.
- 線程和進程最根本的區(qū)別在于:進程是資源分配的單位,線程是調度和執(zhí)行的單位.
- 多進程: 在操作系統(tǒng)中能同時運行多個任務(程序).
- 多線程: 在同一應用程序中有多個順序流同時執(zhí)行.
- 線程是進程的一部分,所以線程有的時候被稱為輕量級進程.
- 一個沒有線程的進程是可以被看作單線程的,如果一個進程內擁有多個線程,進程的執(zhí)行過程不是一條線(線程)的,而是多條線(線程)共同完成的.
- 系統(tǒng)在運行的時候會為每個進程分配不同的內存區(qū)域,但是不會為線程分配內存(線程所使用的資源是它所屬的進程的資源),線程組只能共享資源.
線程, 進程, 協(xié)程之間區(qū)別如圖所示:
三、同步 & 異步
同步和異步強調的是消息通信機制。
1. 同步
同步(synchronous):A調用B,等待B返回結果后,A繼續(xù)執(zhí)行.在同步的過程中, 存在一個等待的狀態(tài). 即: 某個事情執(zhí)行時需要等待另一個個事情的結果, 才能繼續(xù)向下執(zhí)行。
以打電話為例:A向B打電話時, 發(fā)出電話邀請, 只有B同意接聽之后才會進行通話, 否則會一直處于等待狀態(tài)(阻塞). 這一過程稱為同步。
2. 異步
異步(asynchronous ):A調用B,A繼續(xù)執(zhí)行,不等待B返回結果;B有結果了,通知A,A再做處理.
以發(fā)短信為例:A在給B發(fā)消息, 無需等待B的反饋, 便可以給C發(fā)消息. 這一過程成為異步.
今日冒險片段下:
在這千鈞一發(fā)之際, 了不起突然想到了之前奧菲利亞送的卷軸. 利用這個卷軸可以召喚精靈王伊莎貝拉的虛影, 并存在一段時間. 于是二人便利用精靈王的虛影拖住影子劍士剎影, 先將實力較弱的狩魂者和莫比擊敗, 然后在三對一的情況下, 花費接近一天的時間, 艱難的將這個劍士擊敗. 在未來的某一刻, 想到這里, 他們肯定會自豪, 因為他們擊敗的是一個能自由穿梭時空的職業(yè), 并精通鬼劍士四系職業(yè)的第五職業(yè)——暗黑武士. 就這樣, 擊敗領主剎影之后, 了不起也順利的晉升到了lv29.