iOS多線程詳解(一)--- 多線程基礎

前言

最近想系統的研究下iOS上的多線程,就搜集了大量的資料,在這些資料的基礎之上形成了這篇文章。一方面希望自己加深下印象,另外一方面也希望對他人提供一些幫助。由于本人水平有限,難免出現錯誤和疏漏之處,還請讀者見諒。本篇文章主要參考了李明杰老師的教程和一些優秀的文章,再此深表感謝。

1、基本概念

  • 進程
    進程是指在系統中正在運行的一個應用程序。例如,同時打開QQ和Xcode,系統就會分別啟動2個進程。在MAC電腦上,可以通過活動監視器查看進程。


    活動監視器.png
  • 線程
    一個進程想要執行任務,必須得有線程(每1個進程至少要有1條線程)。線程是進程的基本執行單元,一個進程(程序)的所有任務都在線程中執行。例如,使用網易云音樂播放音樂、使用迅雷下載電影等都需要在線程中進行。

  • 主線程
    處理UI,所有更新UI的操作都必須在主線程上執行。不要把耗時操作放在主線程,會卡界面。

  • 線程的串行
    一個線程中的任務的執行是串行的。如果要在1個線程中執行多個任務,那么只能一個一個地按順序執行這些任務。也就是說,在同一時間內,1個線程只能執行1個任務。

  • 多線程
    一個進程中可以開啟多條線程,每條線程可以并行(同時)執行不同的任務。進程類似于實際生活中的車間,而線程可以理解為車間中的工人。

2、線程的狀態與生命周期

下圖是線程狀態示意圖,從圖中可以看出線程的生命周期是:新建-就緒-運行-阻塞-死亡


線程狀態.png

下面分別闡述線程生命周期中的每一步

  • 新建:實例化線程對象
  • 就緒:向線程對象發送start消息,線程對象被加入可調度線程池等待CPU調度。
  • 運行:CPU負責調度可調度線程池中線程的執行。線程執行完成之前,狀態可能會在就緒和運行之間來回切換。就緒和運行之間的狀態變化由CPU負責,程序員不能干預。
  • 阻塞:當滿足某個預定條件時,可以使用休眠或鎖,阻塞線程執行。sleepForTimeInterval(休眠指定時長),sleepUntilDate(休眠到指定日期),@synthesized(self):(互斥鎖)
  • 死亡:正常死亡,線程執行完畢。非正常死亡,當滿足某個條件后,在線程內部中止執行/在主線程中止線程對象。
  • 線程的exit和cancel:
    [NSThread exit]:一旦強行終止線程,后續的所有代碼都不會被執行。
    [thread cancek]:并不會直接取消線程,只是給線程對象添加isCancelled標記。

3、多線程的原理

在同一時間內,(單核)CPU只能處理1條線程,只有1條線程在工作(執行)。因此,多線程并發(同時)執行,其實是CPU快速地在多條線程之間調度(切換)。如果CPU調度線程的時間足夠快,就造成了多線程并發執行的假象。
思考:如果線程非常非常多,會發生什么情況?
1、CPU會在N多線程之間調度,CPU會累死,消耗大量的CPU資源
2、每條線程被調度執行的頻次會降低(線程的執行效率降低)

4、多線程的優缺點

  • 多線程的優點
    1、能適當提高程序的執行效率
    2、能適當提高資源利用率(CPU、內存利用率)

  • 多線程的缺點
    1、開啟線程需要占用一定的內存空間(默認情況下,主線程占用1M,子線程占用512KB),如果開啟大量的線程,會占用大量的內存,降低程序的性能
    2、線程越多,CPU在調度線程上的開銷就越大
    3、程序設計會更加復雜(比如線程之間的通信、多線程的數據共享)

5、多線程在iOS開發中的應用

  • 什么是主線程
    一個iOS程序運行后,默認會開啟1條線程,稱之為“主線程”或“UI線程”

  • 主線程的主要作用
    1、顯示/刷新UI界面
    2、處理UI事件(比如點擊事件、滾動事件、拖拽事件等)

  • 主線程的使用注意
    1、不要將比較耗時的操作放到主線程中,因為耗時操作會卡住主線程,嚴重影響UI的流暢度,給用戶一種“卡”的壞體驗。
    2、將耗時操作放在子線程(后臺線程、非主線程)中執行

6、多線程的四種解決方案

多線程的四種解決方案分別是:pthread,NSThread,GCD,NSOperation。
下圖給出了這四種方案的解讀和對比。


多線程的四種解決方案.png

7、線程安全問題

當多個線程訪問同一塊資源時,很容易引發數據錯亂和數據安全問題。就好比幾個人在同一時修改同一個表格,造成數據的錯亂。

解決多線程安全問題的方法

  • 方法一:互斥鎖(同步鎖)
@synchronized(鎖對象) {
    // 需要鎖定的代碼
}

判斷的時候鎖對象要存在,如果代碼中只有一個地方需要加鎖,大多都使用self作為鎖對象么這樣可以避免單獨再創建一個鎖對象。
加了互斥鎖的代碼,當新的線程訪問時,如果發現其他線程正在執行鎖定的代碼,新線程就會進入休眠。

  • 方法二:自旋鎖
    加了自旋鎖,當新線程訪問代碼時,如果發現有其他線程正在鎖定代碼,新線程就會用死循環的方式,一直等待鎖定的代碼執行完成。相當于不停嘗試執行代碼,比較消耗性能。
    屬性修飾atomic本身就有一把自旋鎖。
    下面說明了屬性修飾nonatomic和atomic

nonatomic 非原子屬性,同一時間可以有很多線程讀和寫
atomic 原子屬性(線程安全),保證同一時間只有一個線程能夠寫入(但是同一個時間多個線程都可以取值),atomic 本身就有一把鎖(自旋鎖)
atomic:線程安全,需要消耗大量的資源
nonatomic:非線程安全,不過效率更高,一般使用nonatomic

8、小結

本節主要介紹了多線程的基礎知識,從下節開始介紹iOS多線程的具體使用。

相關系列文章

iOS多線程詳解(一)--- 多線程基礎
iOS多線程詳解(二)--- pthread&NSThread
iOS多線程詳解(三)--- GCD
iOS多線程詳解(四)--- NSOperation
iOS多線程詳解(五)--- 線程安全(鎖的創建)
iOS多線程詳解(六)--- 線程安全(Property)

參考資料:

1、李明杰老師的iOS視頻教程
2、http://www.cocoachina.com/ios/20170707/19769.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。