iOS 多線程編程(一)多線程基礎

多線程系列篇章計劃內容:
iOS多線程編程(一) 多線程基礎
iOS多線程編程(二) Pthread
iOS多線程編程(三) NSThread
iOS多線程編程(四) GCD
iOS多線程編程(五) GCD的底層原理
iOS多線程編程(六) NSOperation
iOS多線程編程(七) 同步機制與鎖
iOS多線程編程(八) RunLoophtml

現代計算機系統中,CPU做爲計算機系統的運算控制核心,是信息處理、程序運行的最終執行單元。操做系統做爲計算機的管理者,負責任務的調度資源的分配和管理,協調着各個硬件(如CPU、內存,硬盤、網卡等)有序的工做着。程序員

在瞭解進程和線程以前,咱們不妨先從操做系統開始。shell

1、操做系統

1.1 操做系統概念

操做系統是管理計算機硬件與軟件資源計算機程序。做爲計算機最基本也最爲重要的」軟件「系統,操做系統須要處理如管理與配置內存決定系統資源的供需的優先次序控制輸入設備與輸出設備操做網絡與管理文件系統等基本事務。操做系統也提供一個讓用戶與系統交互的操做界面。編程

從計算機用戶的角度來講,計算機操做系統體現爲其提供的各項服務;從程序員的角度來講,其主要是指用戶登陸的界面或者接口;若是從設計人員的角度來講,就是指各式各樣模塊和單元之間的聯繫。瀏覽器

1.2 操做系統發展的幾個階段

1.2.1 無操做系統階段

最初的電腦沒有操做系統,在這個階段,計算機都是人工的去操做的,人們經過各類按鈕來控制計算機,在操做的時候,每一個用戶都會獨佔這臺計算機。而且計算機的CPU是等待人工去操做的,當用戶進行輸入和輸出的時候,內存CPU都是空閒的。markdown

所以,在沒有操做系統的時代,計算機資源的利用率很低網絡

1.2.2 批處理系統階段

後來出現了彙編語言,操做人員經過有孔的紙帶將程序輸入電腦進行編譯。 在批處理系統階段,計算機就無須再等待人工的輸入了。由於這個時候用戶會批量的導入任務,這樣,用戶在導入任務以後就能夠離開,計算機就能夠自動的去工做。多線程

雖然在這個批處理系統裏面,咱們能夠將任務批量的輸入,可是這個時候計算機只能同時運行一個任務。也就是說,雖然任務是批處理輸入的,可是計算機每次仍是隻能執行一個任務。併發

爲了解決這一問題,在這個階段提出了一個很是重要的概念,叫多道程序設計oop

多道程序設計

多道程序設計指的是在計算機內存中同時存放多個程序,而且這多個程序互相不干擾。 這裏的多道程序在計算機的管理程序之下相互穿插運行,以此來提高計算機資源的利用率。

1.2.3 分時系統階段

在這個階段,最重要的設計就是人機交互的設計,在前面兩個階段,程序在執行的過程當中,人是沒有辦法去幹預的,在分時系統階段,人機就能夠進行交互了。而且,人能夠實時的去調試程序,這個分時系統容許多個用戶去共享這個計算機的資源,所以在這個階段,計算的資源利用率大幅度提高。分時系統也是如今主流的系統。

1.3 操做系統的功能與意義

操做系統主要包括如下幾個方面的功能 :

① 進程管理

其工做主要是進程調度,在單用戶單任務的狀況下,處理器僅爲一個用戶的一個任務所獨佔, 進程管理的工做十分簡單。但在多道程序或多用戶的狀況下,組織多個做業或任務時,就要解決處理器的調度、分配和回收等問題。

② 存儲管理

分爲幾種功能:存儲分配、存儲共享、存儲保護 、存儲擴張。

③ 設備管理

分有如下功能:設備分配、設備傳輸控制 、設備獨立性。

④ 文件管理

文件存儲空間的管理、目錄管理 、文件操做管理、文件保護。

⑤ 做業管理

負責處理用戶提交的任何要求。

計算機的操做系統對於計算機能夠說是十分重要的。

  • 操做系統會統一管理着計算機資源

好比咱們須要計算機計算」1+1「,並非直接告訴CPU說咱們要算"1+1",而是藉助操做系統,讓操做系統去告訴硬件咱們要作什麼事情; 再好比咱們存儲或讀取一個文件的時候,咱們不是直接去控制存儲器裏邊的機械設備讀取的,而是經過操做系統去讀寫這些信息的。
操做系統實現了對計算機資源的抽象。這個抽象就是經過管理軟件來實現的,這些管理軟件屏蔽了硬件設備,而且給用戶提供了邏輯設備,使得每一個用戶在使用的時候都是同樣的。

  • 用戶無需面向硬件接口編程

計算機發展到如今,設備種類繁多複雜。操做系統提供統一的操做界面,屏蔽了不一樣設備之間的差別。有了操做系統咱們就不須要關注不一樣的設備,也不須要關注不一樣的接口。
也就是說咱們無需面向存儲器、IO設備這些硬件,而只需面向操做系統去編程就能夠了。 舉個例子,在操做系統裏邊有IO設備管理軟件,這個軟件提供了讀寫接口,用戶在進行編程時,直接調用這個接口,並不須要具體的接觸某個IO設備。

  • 操做系統提供了用戶與計算機之間的接口

在不一樣的設備上,操做系統可向用戶呈現多種操做手段。如:

圖形窗口形式(好比在手機上,咱們能夠經過手指的觸摸控制手機裏的硬件設備(如攝像頭、聲音)、而在咱們普通的PC端,咱們主要是經過鼠標、鍵盤來控制硬件)。)

命令形式(Linux中經過在shell終端中輸入命令的方式)

系統調用形式(主要是編程的時候,好比打開文件、讀取數據這些操做,都是經過系統調用來完成的) 操做系統的簡易性使得更多人可以使用計算機。更多的人可使用計算機就意味着解放和發展的生產力,對人類科技的提高是大有幫助的。

1.4 小結

在沒有操做系統的時代,資源只屬於當前運行的程序,且計算機只能運行一個程序,計算機資源利用率很低。

有了多道程序設計的概念,爲了實現程序的共用,以及對計算機資源的管理,便有了操做系統。

2、進程與線程

也是基於多道程序設計的概念,爲了使多個程序能併發執行,以提升資源的利用率和系統的吞吐量。進程也就出現了,進程的做用就是合理的隔離資源(由於有多道程序的概念,因此在計算機中就可能會有多個進程共同的使用某一個物理設備,好比存儲器。那麼進程在這裏面就是發揮運行資源隔離的做用)、提高資源利用率

一個程序在運行過程當中會涉及不少操做,利用 CPU 計算、經過磁盤 IO 進行數據傳輸等等,咱們知道當程序在進行磁盤 IO 的時候,由於速度問題,會比較慢,所在在這個過程當中 CPU 會空閒下來,這會形成資源的浪費,正由於引入進程,在 A 進程進行磁盤 IO 的時候,會讓出 CPU 給 B 進程,合理地利用了 CPU 資源,使得程序之間能夠併發執行。

2.1 進程

進程(Process)是計算機中具備必定獨立功能的程序關於某個數據集合的一次運行活動。它能夠申請和擁有系統資源,是系統進行資源分配和調度的基本單位(有了多道程序的概念,操做系統就能夠對每一個程序進行資源的分配)。

從狹義上,能夠將進程理解爲正在運行的程序的實例,當一個程序進入內存運行時,系統就會建立一個進程,併爲它分配資源,而後把該進程放入進程就緒隊列,進程調度器選中它的時候就會爲它分配CPU時間,程序開始真正運行。

下圖爲Mac系統下的活動監視器

從圖片來看,每個進程都佔有 CPU內存能耗磁盤網絡等資源。

每個進程都有它本身的地址空間,通常狀況下,進程由 3 個部分組成,分別是程序代碼數據集、棧進程控制塊(Process Control Block)。

各自的做用以下:

  • 程序代碼:描述進程要完成哪些功能以及如何完成。
  • 數據集、棧:程序在執行時所須要的數據和工做區。
  • 進程控制塊(PCB):包含進程的描述信息和控制信息。用來記錄進程的外部特徵,描述進程的執行變化過程,系統能夠利用它來控制和管理進程,它是系統感知進程存在的惟一標誌。

進程具備的特徵:

  • 併發性:任何進程均可以同其餘進行一塊兒併發執行;
  • 動態性:進程是程序的一次執行過程,是臨時的,有生命期的,是動態產生,動態消亡的;
  • 獨立性:進程是系統進行資源分配和調度的一個獨立單位;
  • 結構性:進程由程序,數據和進程控制塊三部分組成

2.2 線程

在早期的操做系統中並無線程的概念,進程是擁有資源和獨立運行的最小單位。任務調度採用的是時間片輪轉搶佔式調度方式,而進程做爲任務調度的最小單位,每一個進程有各自獨立的一塊內存,使得各個進程之間內存地址相互隔離。

後來,隨着計算機技術的發展,可運行的進程愈來愈多。進程出現了不少弊端,一是因爲進程是資源擁有者,建立、撤消與切換存在較大的時空開銷,所以須要引入輕型進程;二是因爲對稱多處理機(SMP)的出現,能夠知足多個運行單位,而多個進程並行開銷過大。所以出現了能獨立運行的基本單位——線程(Threads)

線程是程序執行中一個單一順序控制流程,是程序執行流的最小單元,是處理器調度和分派的基本單位。

在引入線程的操做系統中,一般都是把進程做爲分配資源的基本單位,而把線程做爲獨立運行和獨立調度的基本單位。因爲線程比進程更小,基本上不擁有系統資源,故對它的調度所付出的開銷就會小得多,能更高效的提升系統內多個程序間併發執行的程度,從而顯著提升系統資源的利用率和吞吐量

2.3 進程與線程的關係:

一個正在運行的軟件(如迅雷)就是一個進程,一個進程能夠同時運行多個任務( 迅雷軟件能夠同時下載多個文件,每一個下載任務就是一個線程)。他們的關係以下:

  • 線程是依附於進程的,不能獨立存在,它包含在進程之中,是進程中的實際運做單位。進程一旦結束,全部線程都結束。

  • 一個線程只能屬於一個進程,而一個進程能夠有多個線程,但至少有一個線程。

  • 線程是進程中的一個執行單元,由CPU獨立調度執行,負責當前進程中任務的執行。一個進程能夠有一個或多個線程,線程會擁有本身的堆棧局部變量(不共享),可是它與同一進程中的多個線程將共享程序的內存空間,也就是該進程中的代碼段(代碼和常量),數據段(全局變量和靜態變量),擴展段(堆存儲)等系統資源。

引用一個例子:

若是把上課的過程比做進程,把老師比做CPU,那麼能夠把每一個學生比做每一個線程,全部學生共享這個教室(也就是全部線程共享進程的資源),上課時學生A向老師提出問題,老師對A進行解答,此時可能會有學生B對老師的解答不懂會提出B的疑問(注意:此時可能老師尚未對A同窗的問題解答完畢),此時老師又向學生B解惑,解釋完以後又繼續回答學生A的問題,同一時刻老師只能向一個學生回答問題(即:當多個線程在運行時,同一個CPU在某一個時刻只能服務於一個線程,可能一個線程分配一點時間,時間到了就輪到其它線程執行了,這樣多個線程在來回的切換)

2.4 進程和線程的區別:

  • ①. 根本區別:進程是操做系統分配資源的最小單位,線程程序執行的最小單位。

全部與該進程有關的資源,都被記錄在進程控制塊(PCB)中。以表示該進程擁有這些資源或正在使用它們。進程也是搶佔處理機的調度單位,它擁有一個完整的虛擬地址空間。當進程發生調度時,不一樣的進程擁有不一樣的虛擬地址空間,而同一進程內的不一樣線程共享同一地址空間。

與進程相對應,線程與資源分配無關,它屬於某一個進程,並與進程內的其餘線程一塊兒共享進程的資源。 一個標準的線程由線程ID、當前指令指針PC、寄存器、堆棧線程控制表TCB組成。 而進程由內存空間(代碼,數據,進程空間,打開的文件)和一個或多個線程組成。

  • ②. 地址空間和其它資源: 進程之間相互獨立,但同一進程下的各線程共享程序的內存空間及一些進程級的資源。某進程內的線程在其餘進程不可見。

  • ③. 調度和切換:線程上下文切換比進程上下文切換要快得多。

  • ④. 通訊機制:進程間通訊,因爲它們具備獨立的數據空間,須要進程同步和互斥手段的輔助,以保證數據的一致性,方式不只耗時,並且不方便;但同一進程下的線程之間數據共享,能夠直接讀寫進程數據段(如全局變量)來進行通訊;

  • ⑤. 內存分配:系統在運行的時候會爲每一個進程分配不一樣的內存空間,而線程除了CPU外,不會再分配空間,而是使用進程的資源空間。

2.5 舉個例子:

3、多進程與多線程

現代操做系統,好比Mac OSUNIXLinuxWindows等,都是支持「多任務」的操做系統。

也就是操做系統能夠同時運行多個任務。好比,你能夠一邊用瀏覽器上網,一邊聽音樂,一邊寫代碼,對於操做系統而言,這就是多任務。

在早期單核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用於處理界面刷新,這樣就可使得各個組件能夠「並行」的運行了。

自此以後,多線程技術獲得普遍應用。

3.1 多進程 vs 多線程

多任務既能夠由多進程實現,也能夠由單進程內的多線程實現,還能夠混合多進程+多線程。混合多進程和多線程的程序涉及到同步、數據共享的問題,這種模型更復雜,實際不多采用。

和多進程相比,多線程的優點在於:

  • 線程的調度與切換比進程不少,同時建立一個線程的開銷也比進程要小不少;
  • 線程之間的通訊更方便,同一進程下的線程共享全局變量、靜態變量等數據,線程間通訊就是讀寫同一個變量,速度很快。而進程之間的通訊須要以通訊的方式(Inter Process Communication,IPC)進行。

多進程的優勢在於:

  • 多進程程序更健壯,在多進程的狀況下,一個進程崩潰不會影響其餘進程,而在多線程的狀況下,任何一個線程崩潰會直接致使整個進程崩潰。

3.2 多線程的意義:

多線程最大的意義,就是最大限度地利用CPU資源。

  • 某個操做可能會陷入長時間等待,等待的線程會進入睡眠狀態,沒法繼續執行。多線程執行能夠有效利用等待時間進行線程切換。如等待網絡響應。
  • 某個操做可能會消耗大量的時間,若是隻有一個線程,程序和用戶之間的交互會被中斷。多線程可讓一個線程負責交互另外一個線程負責計算
  • 程序自己要求併發操做,如一個多端下載軟件(如Bittorrent)。
  • 多CPU或多核處理器,自己具有同時執行多個線程的能力,所以單線程程序沒法全面發揮計算機的所有計算能力。
  • 多線程能夠提升程序的效率。多線程同步完成多項任務,不是爲了提升運行效率,而是爲了提升資源使用效率來提升系統的效率。

固然,多線程也並非百利而無害的。

  • 開啓線程須要佔用必定的內存空間(默認狀況下,每條線程佔512kb);若是開啓大量的線程,會佔用大量的內存空間,下降程序的性能。
  • 線程越多,CPU在調用線程上的開銷就越大(由於要在線程之間切換);
  • 多線程編程的程序設計會更加複雜(如線程間的通訊、多線程的數據共享等)。使用不當,可能會帶來更多的bug

3.3 並行與併發:

並行(parallel):指在同一時刻,有多條指令在多個處理器上同時執行;

併發(concurrency):指在同一時刻只能有一條指令執行,但多個進程指令被快速的輪換執行,使得在宏觀上具備多個進程同時執行的效果,但在微觀上並非同時執行的,只是把時間分紅若干段,使多個進程快速交替的執行

真正的並行執行多任務只能在多核CPU上實現,可是,因爲任務數量遠遠多於CPU的核心數量,因此,操做系統也會自動把不少任務輪流調度到每一個核心上執行。

4、 線程的狀態(生命週期)

4.1 新建態(new)

建立一個Thread對象就生成一個新線程。建立完成後就須要爲線程分配內存。當線程處於"新線程"狀態時,僅僅是一個空線程對象,它尚未分配到系統資源。所以只能啓動或終止它。任何其餘操做都會引起異常。

例如,一個線程調用了new方法,並在調用start方法以前的就處於新線程狀態,能夠調用startstop方法。

4.2 就緒態(Runnable)

一個新建立的線程並不自動開始運行,要執行線程,必須調用線程的start()方法。當線程對象調用start()方法即啓動了線程,建立線程運行所必須的系統資源,並調度線程執行run()方法 ,當start()方法返回後,線程就處於就緒狀態

處於就緒狀態的線程並不必定當即運行run()方法,線程還必須同其餘線程競爭CPU時間,只有得到CPU時間才能夠運行線程。由於在單CPU的計算機系統中,不可能同時運行多個線程,一個時刻僅有一個線程處於運行狀態。所以,此時可能有多個線程處於就緒狀態。

4.3 運行態(Running)

當線程得到CPU時間後,它才進入運行狀態,真正開始執行run()方法。

4.4 阻塞態(Blocked)

所謂阻塞狀態是正在運行的線程沒有運行結束,暫時讓出CPU,這時其餘處於就緒狀態的線程就能夠得到CPU時間,進入運行狀態。

將線程掛起(sleep()、wait()、join()、沒有獲取到鎖都會使線程阻塞),可能將資源交給其它線程使用。

suspend()方法被調用。

sleep()方法被調用。

③ 線程使用wait()來等待條件變量。

④ 線程處於I/O請求的等待。

⑤ 線程試圖獲得一個,而該鎖正被其餘線程持有。

4.5 死亡態(Dead)

有兩個緣由會致使線程死亡:

run()方法正常退出而天然死亡;

② 發生異常或者被打斷interrupt()致使線程終止。

run()方法返回,或別的線程調用stop()方法,線程進入死亡態。一般Apple使用它的stop()方法來終止它產生的全部線程。

拓展閱讀:

進程和線程

進程知多少?

相關文章
相關標籤/搜索