iOS 多線程(線程的生命周期)

sandag 發佈 2020-01-27T22:27:48+00:00

性能比較互斥鎖NSLock:是Foundation框架中以對象形式暴露給開發者的一種鎖,NSLock定義如圖,tryLock 和 lock 方法都會請求加鎖,唯一不同的是trylock在沒有獲得鎖的時候可以繼續做一些任務和處理。

  • 進程(Process)是計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是作業系統結構的基礎。在早期面向進程設計的計算機結構中,進程是程序的基本執行實體;在當代面向線程設計的計算機結構中,進程是線程的容器。程序是指令、數據及其組織形式的描述,進程是程序的實體。
  • 進程是指在系統中正在運行的一個應用程式
  • 每個進程相互是獨立的,每個進程均運行在其專用的且受保護的內存

線程的定義

  • 線程(英語:thread)是作業系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以並發多個線程,每條線程並行執行不同的任務。在Unix System V及SunOS中也被稱為輕量進程(lightweight processes),但輕量進程更多指內核線程(kernel thread),而把用戶線程(user thread)稱為線程。
  • 在同一進程中的各個線程,都可以共享該進程所擁有的資源,這首先表現在:所有線程都具有相同的地址空間(進程的地址空間),這意味著,線程可以訪問該地址空間的每一個虛地址;此外,還可以訪問進程所擁有的已打開文件、定時器、信號量機構等。由於同一個進程內的線程共享內存和文件,所以線程之間互相通信不必調用內核。
  • 線程是進程的基本執行單位,一個進程的所有任務都在線程中執行
  • 進程要想執行任務,必須得有線程,進程至少要有一條線程
  • 程序啟動會默認開啟一條線程,這條線程被稱為主線程或UI線程

進程與線程的關係

  • 地址空間 :同一進程的線程共享本進程的地址空間,而進程之間則是獨立的地址空間
  • 資源擁有 :同一進程內的線程共享本進程的資源如內存、I/O、cpu等,但是進程之間的資源是獨立的。
  • 一個進程崩潰後,在保護模式下不會對其他進程產生影響,但是一個線程崩潰整個進程都死掉。所以多進程要比多線程健壯。
  • 進程切換時,消耗的資源大,效率高。所以涉及到頻繁的切換時,使用線程要好於進程。同樣如果要求同時進行並且又要共享某些變量的並發操作,只能用線程不能用進程
  • 執行過程:每個獨立的進程有一個程序運行的入口、順序執行序列和程序入口。但是線程不能獨立執行,必須依存在應用程式中,由應用程式提供多個線程執行控制。
  • 線程是處理器調度的基本單位,但是進程不是。
  • 兩者均可並發執行。

多線程的意義

  • 優點使用線程可以把占據時間長的程序中的任務放到後台去處理用戶介面可以更加吸引人,這樣比如用戶點擊了一個按鈕去觸發某些事件的處理,可以彈出一個進度條來顯示處理的進度程序的運行速度可能加快在一些等待的任務實現上如用戶輸入、文件讀寫和網絡收發數據等,線程就比較有用了。在這種情況下可以釋放一些珍貴的資源如內存占用等等。
  • 缺點創建子線程是有開銷的,iOS下主要成本包括:內核數據結構(大約1KB)、棧空間(子線程512K、主線程1MB,也可以使用-setStackSize:設置,但必須是4K的倍數,而且最小是16K),創建線程大約需要90毫秒的創建時間如果開啟大量線程,會降低程序的性能線程越多,CPU在調度線程的開銷就越大程序設計更加複雜:比如線程之間的通信、多線程的數據共享

多線程的原理

  • 同一時間,CPU只能處理一條線程,只有一條線程在工作(執行)
  • 多線程並發(同時)執行,其實就是CPU執行快速地在多條線程之間調度(切換)
  • 下圖是多線程的圖解,CPU在線程1、線程2、線程3、線程4中不斷的切換,這樣就可以達到一個並發的效果

多線程的生命周期

線程的生命周期是:新建 - 就緒 - 運行 - 阻塞 - 死亡

  • 新建 :實例化線程對象
  • 就緒 :向線程對象發送start消息,線程對象被加入可調度線程池等待CPU調度。
  • 運行 :CPU 負責調度可調度線程池中線程的執行。線程執行完成之前,狀態可能會在就緒和運行之間來回切換。就緒和運行之間的狀態變化由CPU負責,程式設計師不能干預。
  • 阻塞 :當滿足某個預定條件時,可以使用休眠或鎖,阻塞線程執行。sleepForTimeInterval(休眠指定時長),sleepUntilDate(休眠到指定日期),@synchronized(self):(互斥鎖)。
  • 死亡 :正常死亡,線程執行完畢。非正常死亡,當滿足某個條件後,在線程內部中止執行/在主線程中止線程對象

還有線程的exit和cancel [NSThread exit]:一旦強行終止線程,後續的所有代碼都不會被執行。

[thread cancel]取消:並不會直接取消線程,只是給線程對象添加 isCancelled 標記。

下圖表示線程的生命周期

線程的執行流程如下:

  • 新建 - 就緒(在可調度線程池中,等待被CPU的調度執行) - 運行
  • 運行 - CPU切換到其他的線程 - 就緒
  • 運行 - 調用的sleep方法 - 阻塞 - sleep的時間到了 - 就緒
  • 運行 - 任務執行完成 - 死亡
  • 運行 - exit - 死亡

線程加入到可調度線程池過程

多線程實現方案

iOS中實現多線程的方案有以下四種

pthread 開啟線程

pthread_t tid;
int tret = pthread_create(&tid, NULL, thread_run, NULL);
複製代碼

NSThread 開啟線程

//繼承自NSObject的類的對象 都可以調用這個方法 只不過拿不到線程對象
[self performSelectorInBackground:@selector(NSThreadDemo:) withObject:@"NSObjectCategory"];

// 通過類方法創建 分離出來一個線程 不需要手動開啟線程  自動開啟線程並且執行方法
[NSThread detachNewThreadSelector:@selector(NSThreadDemo:) toTarget:self withObject:@"classMethodThread"];

// 通過對象方法來創建線程 並且需要手動啟動線程
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(NSThreadDemo:) object:@"objectMethodThread"];
//手動啟動線程
[thread start];
複製代碼

GCD 開啟線程

// 串行隊列的創建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);

// 並發隊列的創建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);

// 同步執行任務創建方法
dispatch_sync(queue, ^{
    // 這裡放同步執行任務代碼
});

// 異步執行任務創建方法
dispatch_async(queue, ^{
    // 這裡放異步執行任務代碼
});
複製代碼

NSOperation 開啟線程

//NSInvocationOperation   封裝操作
NSInvocationOperation *operation=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test) object:nil];

//執行操作
[operation start];

//創建NSBlockOperation操作對象
NSBlockOperation *operation=[NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"NSBlockOperation------%@",[NSThread currentThread]);
}];

//添加操作
[operation addExecutionBlock:^{
    NSLog(@"NSBlockOperation1------%@",[NSThread currentThread]);
}];

[operation addExecutionBlock:^{
    NSLog(@"NSBlockOperation2------%@",[NSThread currentThread]);
}];

//開啟執行操作
[operation start];
複製代碼

iOS 中的鎖

鎖的概念定義

  • 臨界區 :指的是一塊對公共資源進行訪問的代碼,並非一種機制或是算法。
  • 自旋鎖 :是用於多線程同步的一種鎖,線程反覆檢查鎖變量是否可用。由於線程在這一過程中保持執行,因此是一種忙等待。一旦獲取了自旋鎖,線程會一直保持該鎖,直至顯式釋放自旋鎖。自旋鎖避免了進程上下文的調度開銷,因此對於線程只會阻塞很短時間的場合是有效的。
  • 互斥鎖(Mutex) :是一種用於多線程編程中,防止兩條線程同時對同一公共資源(比如全局變量)進行讀寫的機制。該目的通過將代碼切片成一個一個的臨界區而達成。
  • 讀寫鎖 :是電腦程式的並發控制的一種同步機制,也稱「共享-互斥鎖」、「多讀者-單寫者鎖」,用於解決多線程對公共資源讀寫問題。讀操作可並發重入,寫操作是互斥的。讀寫鎖通常用互斥鎖、條件變量、信號量實現。
  • 信號量(semaphore) :是一種更高級的同步機制,互斥鎖可以說是semaphore在僅取值0/1時的特例。信號量可以有更多的取值空間,用來實現更加複雜的同步,而不單單是線程間互斥。
  • 條件鎖 :就是條件變量,當進程的某些資源要求不滿足時就進入休眠,也就是鎖住了。當資源被分配到了,條件鎖打開,進程繼續運行。

性能比較

互斥鎖

  • NSLock :是Foundation框架中以對象形式暴露給開發者的一種鎖,(Foundation框架同時提供了NSConditionLock,NSRecursiveLock,NSCondition)NSLock定義如圖,tryLock 和 lock 方法都會請求加鎖,唯一不同的是trylock在沒有獲得鎖的時候可以繼續做一些任務和處理。lockBeforeDate方法也比較簡單,就是在limit時間點之前獲得鎖,沒有拿到返回NO。
  • pthread_mutex :實際運用項目如下
  • @synchronize :實際項目中:AFNetworking中 isNetworkActivityOccurring屬性的getter方法
關鍵字: