多線程系列篇章計劃內容:
iOS多線程編程(一) 多線程基礎
iOS多線程編程(二) Pthread
iOS多線程編程(三) NSThread
iOS多線程編程(四) GCD
iOS多線程編程(五) GCD的底層原理
iOS多線程編程(六) NSOperation
iOS多線程編程(七) 同步機制與鎖
iOS多線程編程(八) RunLoophtml
現代計算機系統中,CPU
做爲計算機系統的運算
和控制
核心,是信息處理、程序運行的最終執行單元。操做系統
做爲計算機的管理者,負責任務的調度
,資源的分配和管理
,協調着各個硬件(如CPU、內存,硬盤、網卡等)有序的工做着。程序員
在瞭解進程和線程以前,咱們不妨先從操做系統開始。shell
操做系統是管理計算機硬件與軟件資源
的計算機程序
。做爲計算機最基本也最爲重要的」軟件「系統,操做系統須要處理如管理與配置內存
、決定系統資源的供需的優先次序
、控制輸入設備與輸出設備
、操做網絡與管理文件系統
等基本事務。操做系統也提供一個讓用戶與系統交互
的操做界面。編程
從計算機用戶的角度來講,計算機操做系統體現爲其提供的各項服務;從程序員的角度來講,其主要是指用戶登陸的界面或者接口;若是從設計人員的角度來講,就是指各式各樣模塊和單元之間的聯繫。瀏覽器
最初的電腦沒有操做系統,在這個階段,計算機都是人工
的去操做
的,人們經過各類按鈕來控制計算機,在操做的時候,每一個用戶都會獨佔
這臺計算機。而且計算機的CPU
是等待人工去操做的,當用戶進行輸入和輸出的時候,內存
和CPU
都是空閒的。markdown
所以,在沒有操做系統的時代,計算機資源的利用率很低
網絡
後來出現了彙編語言
,操做人員經過有孔的紙帶將程序輸入電腦進行編譯。 在批處理系統
階段,計算機就無須再等待人工的輸入了。由於這個時候用戶會批量的導入任務
,這樣,用戶在導入任務以後就能夠離開,計算機就能夠自動的去工做。多線程
雖然在這個批處理系統
裏面,咱們能夠將任務批量的輸入,可是這個時候計算機只能同時運行一個任務。也就是說,雖然任務是批處理輸入的,可是計算機每次仍是隻能執行一個任務。併發
爲了解決這一問題,在這個階段提出了一個很是重要的概念,叫多道程序設計
。oop
多道程序設計
:多道程序設計指的是在計算機內存中同時存放多個程序,而且這多個程序互相不干擾。 這裏的多道程序在計算機的管理程序之下相互穿插運行,以此來提高計算機資源的利用率。
在這個階段,最重要的設計就是人機交互
的設計,在前面兩個階段,程序在執行的過程當中,人是沒有辦法去幹預的,在分時系統階段,人機就能夠進行交互了。而且,人能夠實時的去調試程序,這個分時系統容許多個用戶去共享
這個計算機的資源
,所以在這個階段,計算的資源利用率大幅度提高
。分時系統也是如今主流的系統。
操做系統主要包括如下幾個方面的功能 :
① 進程管理
其工做主要是進程調度,在單用戶單任務的狀況下,處理器僅爲一個用戶的一個任務所獨佔, 進程管理的工做十分簡單。但在多道程序或多用戶的狀況下,組織多個做業或任務時,就要解決處理器的調度、分配和回收等問題。
② 存儲管理
分爲幾種功能:存儲分配、存儲共享、存儲保護 、存儲擴張。
③ 設備管理
分有如下功能:設備分配、設備傳輸控制 、設備獨立性。
④ 文件管理
文件存儲空間的管理、目錄管理 、文件操做管理、文件保護。
⑤ 做業管理
負責處理用戶提交的任何要求。
計算機的操做系統
對於計算機能夠說是十分重要的。
操做系統會統一管理着計算機資源
好比咱們須要計算機計算」1+1「,並非直接告訴CPU
說咱們要算"1+1",而是藉助操做系統,讓操做系統去告訴硬件咱們要作什麼事情; 再好比咱們存儲或讀取一個文件的時候,咱們不是直接去控制存儲器裏邊的機械設備讀取的,而是經過操做系統去讀寫這些信息的。
操做系統實現了對計算機資源的抽象。這個抽象就是經過管理軟件來實現的,這些管理軟件屏蔽了硬件設備,而且給用戶提供了邏輯設備,使得每一個用戶在使用的時候都是同樣的。
用戶無需面向硬件接口編程
計算機發展到如今,設備種類繁多複雜。操做系統提供統一的操做界面,屏蔽了不一樣設備之間的差別。有了操做系統咱們就不須要關注不一樣的設備,也不須要關注不一樣的接口。
也就是說咱們無需面向存儲器、IO設備這些硬件,而只需面向操做系統去編程就能夠了。 舉個例子,在操做系統裏邊有IO設備管理軟件,這個軟件提供了讀寫接口,用戶在進行編程時,直接調用這個接口,並不須要具體的接觸某個IO設備。
操做系統提供了用戶與計算機之間的接口
在不一樣的設備上,操做系統可向用戶呈現多種操做手段。如:
圖形窗口形式
(好比在手機上,咱們能夠經過手指的觸摸控制手機裏的硬件設備(如攝像頭、聲音)、而在咱們普通的PC端,咱們主要是經過鼠標、鍵盤來控制硬件)。)
命令形式
(Linux中經過在shell終端中輸入命令的方式)
系統調用形式
(主要是編程的時候,好比打開文件、讀取數據這些操做,都是經過系統調用來完成的) 操做系統的簡易性使得更多人可以使用計算機。更多的人可使用計算機就意味着解放和發展的生產力,對人類科技的提高是大有幫助的。
在沒有操做系統的時代,資源只屬於當前運行的程序,且計算機只能運行一個程序,計算機資源利用率很低。
有了多道程序設計的概念,爲了實現程序的共用,以及對計算機資源的管理,便有了操做系統。
也是基於多道程序設計的概念,爲了使多個程序能併發執行,以提升資源的利用率和系統的吞吐量。進程也就出現了,進程的做用就是合理的隔離資源
(由於有多道程序的概念,因此在計算機中就可能會有多個進程共同的使用某一個物理設備,好比存儲器。那麼進程在這裏面就是發揮運行資源隔離的做用)、提高資源利用率
。
一個程序在運行過程當中會涉及不少操做,利用 CPU 計算、經過磁盤 IO 進行數據傳輸等等,咱們知道當程序在進行磁盤 IO 的時候,由於速度問題,會比較慢,所在在這個過程當中 CPU 會空閒下來,這會形成資源的浪費,正由於引入進程,在 A 進程進行磁盤 IO 的時候,會讓出 CPU 給 B 進程,合理地利用了 CPU 資源,使得程序之間能夠併發執行。
進程(Process)是計算機中具備必定獨立功能
的程序關於某個數據集合
的一次運行活動
。它能夠申請和擁有系統資源,是系統進行資源分配和調度
的基本單位(有了多道程序的概念,操做系統就能夠對每一個程序進行資源的分配)。
從狹義上,能夠將進程理解爲正在運行的程序的實例
,當一個程序進入內存運行時,系統就會建立一個進程,併爲它分配資源,而後把該進程放入進程就緒隊列,進程調度器選中它的時候就會爲它分配CPU時間
,程序開始真正運行。
下圖爲Mac系統下的活動監視器
從圖片來看,每個進程都佔有 CPU
、內存
、能耗
、磁盤
、網絡
等資源。
每個進程都有它本身的地址空間,通常狀況下,進程由 3 個部分組成,分別是程序代碼
、數據集、棧
和進程控制塊
(Process Control Block)。
各自的做用以下:
進程具備的特徵:
在早期的操做系統中並無線程的概念,進程是擁有資源和獨立運行的最小單位。任務調度採用的是時間片輪轉
的搶佔式調度
方式,而進程做爲任務調度的最小單位,每一個進程有各自獨立的一塊內存,使得各個進程之間內存地址相互隔離。
後來,隨着計算機技術的發展,可運行的進程愈來愈多。進程出現了不少弊端,一是因爲進程是資源擁有者,建立、撤消與切換存在較大的時空開銷
,所以須要引入輕型進程;二是因爲對稱多處理機(SMP)的出現,能夠知足多個運行單位,而多個進程並行開銷過大。所以出現了能獨立運行的基本單位——線程(Threads)
。
線程是程序執行中一個單一
的順序控制
流程,是程序執行流的最小單元,是處理器調度和分派
的基本單位。
在引入線程的操做系統中,一般都是把進程
做爲分配資源的基本單位,而把線程
做爲獨立運行和獨立調度的基本單位。因爲線程比進程更小,基本上不擁有系統資源,故對它的調度所付出的開銷就會小得多,能更高效的提升系統內多個程序間併發執行的程度,從而顯著提升系統資源的利用率和吞吐量
。
一個正在運行的軟件(如迅雷)就是一個進程,一個進程能夠同時運行多個任務( 迅雷軟件能夠同時下載多個文件,每一個下載任務就是一個線程)。他們的關係以下:
線程是依附於進程的,不能獨立存在,它包含在進程之中,是進程中的實際運做單位。進程一旦結束,全部線程都結束。
一個線程只能屬於一個進程,而一個進程能夠有多個線程,但至少有一個線程。
線程是進程中的一個執行單元,由CPU
獨立調度執行,負責當前進程中任務的執行。一個進程能夠有一個或多個線程,線程會擁有本身的堆棧
和局部變量
(不共享),可是它與同一進程中的多個線程將共享程序的內存空間
,也就是該進程中的代碼段
(代碼和常量),數據段
(全局變量和靜態變量),擴展段
(堆存儲)等系統資源。
引用一個例子:
若是把上課的過程比做進程,把老師比做CPU,那麼能夠把每一個學生比做每一個線程,全部學生共享這個教室(也就是全部線程共享進程的資源),上課時學生A向老師提出問題,老師對A進行解答,此時可能會有學生B對老師的解答不懂會提出B的疑問(注意:此時可能老師尚未對A同窗的問題解答完畢),此時老師又向學生B解惑,解釋完以後又繼續回答學生A的問題,同一時刻老師只能向一個學生回答問題(即:當多個線程在運行時,同一個CPU在某一個時刻只能服務於一個線程,可能一個線程分配一點時間,時間到了就輪到其它線程執行了,這樣多個線程在來回的切換)
進程
是操做系統分配資源
的最小單位,線程
是程序執行
的最小單位。全部與該進程有關的資源,都被記錄在進程控制塊(PCB)中。以表示該進程擁有這些資源或正在使用它們。進程也是搶佔處理機的調度單位,它擁有一個完整的虛擬地址空間。當進程發生調度時,不一樣的進程擁有不一樣的虛擬地址空間,而同一進程內的不一樣線程共享同一地址空間。
與進程相對應,線程與資源分配無關,它屬於某一個進程,並與進程內的其餘線程一塊兒共享進程的資源。 一個標準的線程由線程ID、當前指令指針PC、寄存器、堆棧線程控制表TCB組成。 而進程由內存空間(代碼,數據,進程空間,打開的文件)和一個或多個線程組成。
②. 地址空間和其它資源: 進程
之間相互獨立,但同一進程下的各線程
共享程序的內存空間及一些進程級的資源。某進程內的線程在其餘進程不可見。
③. 調度和切換:線程
上下文切換比進程
上下文切換要快得多。
④. 通訊機制:進程
間通訊,因爲它們具備獨立的數據空間,須要進程同步和互斥手段的輔助,以保證數據的一致性,方式不只耗時,並且不方便;但同一進程下的線程
之間數據共享,能夠直接讀寫進程數據段(如全局變量)來進行通訊;
⑤. 內存分配:系統在運行的時候會爲每一個進程
分配不一樣的內存空間,而線程
除了CPU外,不會再分配空間,而是使用進程的資源空間。
現代操做系統,好比Mac OS
,UNIX
,Linux
,Windows
等,都是支持「多任務」的操做系統。
也就是操做系統能夠同時運行多個任務。好比,你能夠一邊用瀏覽器上網,一邊聽音樂,一邊寫代碼,對於操做系統而言,這就是多任務。
在早期單核CPU時代尚未線程的概念,只有進程。因爲CPU執行代碼都是順序執行的。那麼,單核CPU是怎麼執行多任務的呢?
答案就是時間片輪轉調度
:簡單地說就是把一個處理器劃分爲若干個短的時間片
,每一個進程
會被操做系統分配一個時間片(即每次被 CPU 選中來執行當前進程所用的時間),每一個時間片依次輪流
地執行處理各個應用程序,時間一到,不管進程是否運行結束,操做系統都會強制將 CPU 這個資源轉到另外一個進程去執行,因爲一個時間片很短,相對於一個應用程序來講,就好像是處理器在爲本身單獨服務同樣,從而達到多個應用程序在同時進行的效果。
如上圖所示,每個小方格表明一個時間片,大約100ms。假設如今我同時開着 Word、QQ、網易雲音樂三個軟件,CPU
首先去處理 Word 進程,100ms時間一到,CPU
就會被強制切換到 QQ 進程,處理100ms後又切換到網易雲音樂進程上,100ms後又去處理 Word 進程,如此往復不斷地切換。表面上看,每一個任務都是交替執行
的,可是,因爲CPU的執行速度實在是太快了,咱們感受就像全部任務都在同時執行
同樣。
隨着運行的進程愈來愈多,人們發現進程的建立、撤銷與切換存在着較大的時空開銷
,所以業界急需一種輕型的進程技術來減小開銷。因而上世紀80年代出現了一種叫 SMP(Symmetrical Multi-Processing)的對稱多處理技術
,就是咱們所知的線程概念。線程
切換的開銷要小不少,這是由於每一個進程都有屬於本身的一個完整虛擬地址空間,而線程隸屬於某一個進程,與進程內的其餘線程一塊兒共享這片地址空間,基本上就能夠利用進程所擁有的資源而無需調用新的資源,故對它的調度所付出的開銷就會小不少。
以 QQ 聊天軟件爲例,上文咱們一直都在說不一樣進程如何流暢的運行,此刻咱們只關注一個進程的運行狀況。若是沒有線程技術的出現,當 QQ 這個進程被 CPU 「臨幸」時,我是該處理聊天呢仍是處理界面刷新呢?若是隻處理聊天,那麼界面就不會刷新,看起來就是界面卡死了。有了線程技術後,每次 CPU 執行100ms,其中30ms用於處理聊天,40ms用於處理傳文件,剩餘的30ms用於處理界面刷新,這樣就可使得各個組件能夠「並行」的運行了。
自此以後,多線程技術獲得普遍應用。
多任務既能夠由多進程
實現,也能夠由單進程內的多線程
實現,還能夠混合多進程+多線程
。混合多進程和多線程的程序涉及到同步、數據共享的問題,這種模型更復雜,實際不多采用。
和多進程相比,多線程
的優點在於:
調度與切換
比進程快
不少,同時建立一個線程的開銷也比進程要小不少;通訊更方便
,同一進程下的線程共享全局變量、靜態變量等數據,線程間通訊就是讀寫同一個變量,速度很快。而進程之間的通訊須要以通訊的方式(Inter Process Communication,IPC)進行。而多進程
的優勢在於:
更健壯
,在多進程的狀況下,一個進程崩潰不會影響其餘進程,而在多線程的狀況下,任何一個線程崩潰會直接致使整個進程崩潰。多線程最大的意義,就是最大限度地利用CPU資源。
長時間等待
,等待的線程會進入睡眠狀態
,沒法繼續執行。多線程執行能夠有效利用等待時間進行線程切換
。如等待網絡響應。消耗大量的時間
,若是隻有一個線程,程序和用戶之間的交互會被中斷
。多線程可讓一個線程負責交互
,另外一個線程負責計算
。併發操做
,如一個多端下載軟件(如Bittorrent)。多CPU或多核
處理器,自己具有同時執行多個線程的能力,所以單線程程序沒法全面發揮計算機的所有計算能力。提升程序的效率
。多線程同步完成多項任務,不是爲了提升運行效率
,而是爲了提升資源使用效率
來提升系統的效率。固然,多線程也並非百利而無害的。
並行(parallel)
:指在同一時刻
,有多條指令在多個處理器上同時執行
;
併發(concurrency)
:指在同一時刻只能有一條指令執行,但多個進程指令被快速的輪換執行,使得在宏觀上具備多個進程同時執行的效果,但在微觀上並非同時執行的,只是把時間分紅若干段,使多個進程快速交替的執行
。
真正的並行執行
多任務只能在多核CPU上實現,可是,因爲任務數量遠遠多於CPU的核心數量,因此,操做系統也會自動把不少任務輪流調度到每一個核心上執行。
建立一個Thread
對象就生成一個新線程。建立完成後就須要爲線程分配內存。當線程處於"新線程"狀態時,僅僅是一個空線程對象,它尚未分配到系統資源。所以只能啓動或終止它。任何其餘操做都會引起異常。
例如,一個線程調用了new
方法,並在調用start
方法以前的就處於新線程狀態,能夠調用start
和stop
方法。
一個新建立的線程並不自動開始運行,要執行線程,必須調用線程的start()
方法。當線程對象調用start()
方法即啓動了線程,建立線程運行所必須的系統資源,並調度線程執行run()
方法 ,當start()
方法返回後,線程就處於就緒狀態
。
處於就緒狀態
的線程並不必定當即運行run()
方法,線程還必須同其餘線程競爭CPU時間
,只有得到CPU時間
才能夠運行線程。由於在單CPU的計算機系統中,不可能同時運行多個線程,一個時刻僅有一個線程處於運行狀態。所以,此時可能有多個線程處於就緒狀態。
當線程得到CPU時間後,它才進入運行狀態,真正開始執行run()
方法。
所謂阻塞狀態是正在運行的線程沒有運行結束,暫時讓出CPU
,這時其餘處於就緒狀態
的線程就能夠得到CPU時間
,進入運行
狀態。
將線程掛起(sleep()、wait()、join()、沒有獲取到鎖都會使線程阻塞),可能將資源交給其它線程使用。
①suspend()
方法被調用。
②sleep()
方法被調用。
③ 線程使用wait()
來等待條件變量。
④ 線程處於I/O
請求的等待。
⑤ 線程試圖獲得一個鎖
,而該鎖正被其餘線程持有。
有兩個緣由會致使線程死亡:
① run()
方法正常退出
而天然死亡;
② 發生異常
或者被打斷interrupt()
致使線程終止。
當run()
方法返回,或別的線程調用stop()
方法,線程進入死亡態。一般Apple使用它的stop()
方法來終止它產生的全部線程。
拓展閱讀: