多線程
Mac、iPhone的操作系統OS X、iOS根據用戶的指示啟動應用程序后,首先便將包含在應用程序中的CPU命令列配置到內存中。CPU從應用程序知道的地址開始,一個一個執行CPU命令列。
在OC的if或for語句等控制語句或函數調用的情況下,執行命令列的地址會遠離當前的位置(位置遷移)。但是,由于一個CPU一次只能執行一個命令,不能執行某處分開的并列的兩個命令,因此通過CPU執行的CPU命令列就好比一條無分叉的大道,其執行不會出現分歧。
通過CPU執行的CPU命令列.png
這里所說的“1個CPU執行的CPU命令列為一條無分叉路徑”,即為“線程”
這種無分叉路徑不只1條,存在有多條時即為“多線程”。1個CPU核執行多條不同路徑上的不同命令。
在多線程中執行CPU命令列.png
OS X和iOS的核心XNU內核在發生操作系統事件時(如每隔一定時間,喚起系統調用等情況)會切換執行路徑。例如CPU的寄存器等信息保存到各自路徑專用的內存塊中,從切換目標路徑專用的內存塊中,復原CPU寄存器等信息,繼續執行切換路徑的CPU命令列。這被稱為“上下文切換”
由于使用多線程的程序可以在某個線程和其他線程之間反復多次進行上下文切換,因此看上去就好像1個CPU核能夠并列地執行多個線程一樣。而且在具有多個CPU核的情況下,就不是“看上去像”了,而是真的提供了多個CPU核并行執行多個線程的技術。
這種利用多線程編程的技術就被稱為“多線程編程”
但是,多線程編程實際上是一種易發生各種問題的編程技術。比如多個線程更新相同資源會導致數據的不一致(數據競爭)、停止等待事件的線程會導致多個線程相互等待(死鎖)、使用太多線程會消耗大量內存等。
*多線程技術
多線程簡單來說是指一個進程中開啟多個線程,多個線程并發執行(同時執行,并行)的技術。例如一個進程中開啟三個線程同時執行3個不同任務:
*多線程原理
對于單核CPU而言,同一時間只能執行一個線程,只能允許一個線程工作。多線程并發是指在一個進程中開啟了多個線程,CPU在各個線程之間來回調度。當CPU的調度時間足夠快時就產生了多線程并發執行的效果。
*多線程技術的有優點:
1、可以充分利用多核CPU的性能,提供資源的利用率
2、能夠提高程序的運行效率,使程序響應更快。
3、能夠設置不同任務的執行優先級,
*多線程技術的缺點 (數據競爭、死鎖、太多線程導致消耗大量的內存)
1、線程的創建需要CPU的開銷,對線程的管理需要額外的CPU開銷。線程的使用會為系統帶來上下文環境切換帶來額外的負擔。線程越多,CPU在線程調度管理上的開銷就越大,對于移動設備尤甚。
2、線程之間的通信和數據交互復雜。
3、多個線程直接對于資源的使用會導致程序變慢,同時會導致資源的競爭,數據的共享等不安全問題。
。。。
多線程數據安全問題
線程不安全:就是不提供數據訪問保護,有可能出現多個線程先后更改數據造成所得到的數據是臟數據。當多個線程訪問同一塊資源時,很容易引發數據錯亂和數據安全問題。
線程安全:簡單來說就是多個線程同時對共享資源進行訪問時,采用了加鎖機制,當一個線程訪問共享資源,對該資源進行保護,其他線程不能進行訪問直到該線程讀取完,其他線程才可使用。
**線程的幾種存在狀態
線程幾種狀態的切換
新建狀態(New):當新創建一個線程時,此時線程處在新建狀態。程序還沒有開始執行線程中的代碼。
就緒狀態(Runnable):一個新創建的線程并不自動開始運行,要執行線程,必須調用線程的start方法創建線程運行的系統資源,這時候線程就處于就緒狀態。
運行狀態(Running):當線程獲得CPU時間后,它才進入運行狀態,真正開始執行線程中的代碼。
阻塞狀態(Blocked):當線程在運行中遇到:1,調用sleep方法;2,線程調用一個在I/O上被阻塞的操作;3,線程等待同步鎖;4,線程在等待某個觸發條件等幾種情況時,線程就處于阻塞狀態。 所謂阻塞狀態是正在運行的線程沒有運行結束,但是不被CPU調度,暫時讓出CPU資源,這時其他處于就緒狀態的線程就可以獲得CPU調度,進入運行狀態。
死亡狀態(Dead): ** 當線程任務執行完畢或者異常時,線程處于死亡狀態。死亡后的線程將不能在執行任務。
iOS中多線程編程的實現方案有一下幾種:pthread、NSThread、GCD、NSOperation。
GCD
Grand Central Dispatch(牛逼的中樞調度系統),蘋果公司利用C語言開發的多線程實現技術,旨在通過充分利用設備的多核處理器以優化提升應用程序的運行。
GCD的使用步驟:
創建任務
將任務存放到隊列中(GCD根據隊列的FIFO原則自動將隊列中的任務取出,放到對應的線程中執行)
GCD中三個重要的概念:
任務,就是需要執行的操作;
隊列,就是存放任務的容器,其中隊列又被分為并發隊列、串行隊列兩種。
同步函數/異步函數
GCD中隊列和函數:
并發隊列(Concurrent Dispatch Queue)
允許多個任務并發(同時)執行的隊列
//創建一個并發隊列,標志為? www.cqut.queue
dispatch_queue_t queue = dispatch_queue_create("concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
串行隊列(Serial? Dispatch Queue)
串行隊列中的任務只能依次執行
//創建一個串行隊列,標志為? serial.queue
dispatch_queue_t queue = dispatch_queue_create("serial.queue", DISPATCH_QUEUE_SERIAL);
同步函數
在當前線程執行任務,不會開啟新的線程
//同步函數,傳入隊列,執行Block中的任務
dispatch_sync(dispatch_queue_t? _Nonnull queue, ^(void)block)
異步函數
可以開啟新線程并在其中執行任務
//異步函數,傳入隊列,執行Block中的任務
dispatch_async(dispatch_queue_t? _Nonnull queue, ^(void)block)
注意:同步/異步決定是否開啟線程;并發/串行決定任務的執行方式,允不允許并發。根據函數同步/異步調用和任務放置在并發還是串行隊列中我們可以得到下面四種組合:
異步函數 + 并發隊列
開啟多個子線程同時執行多個任務(任務并行)。
異步函數 + 串行隊列
開啟一個子線程,任務在該線程中依次執行(任務串行)。
同步函數 + 并發隊列
不會開啟子線程,此時的隊列由于沒有子線程支持,失去了并發的能力,任務在當前線程中依次執行(任務串行)。
同步函數 + 串行隊列
不會開啟子線程,任務在隊列中依次執行(任務串行)。
GCD中兩個特殊的隊列:
dispatch_get_main_queue():主隊列
GCD默認創建,只需要使用。該函數返回綁定到主線程的默認串行隊列。該隊列中執行的任務必定是在主線程中執行的。
dispatch_get_global_queue(long identifier, unsigned long flags):全局并發隊列
【參數說明】flags:保留在未來使用,默認傳0;
identifier: 分配到隊列的項目運行的優先級,優先級由高到低依次為:DISPATCH_QUEUE_PRIORITY_HIGH、DISPATCH_QUEUE_PRIORITY_DEFAULT、DISPATCH_QUEUE_PRIORITY_LOW、DISPATCH_QUEUE_PRIORITY_BACKGROUND,一般我們使用默認的DISPATCH_QUEUE_PRIORITY_DEFAULT就能滿足需求
該函數返回
下面一張圖簡單概括一下關于同步/異步函數和各種隊列之間排列組合子線程開啟于任務執行情況:
隊列與線程.png
由圖可知開發中最常用的就是異步函數組合并發和串行隊列,
【注意】:使用同步函數(sync)向當前串行隊列中添加任務會卡住當前隊列,導致任務不能正常執行。
GCD中常用的函數
dispatch_barrier_async:在該函數之前的任務必定先執行,在該函數之后的任務后執行(當前隊列不能是全局并發隊列)。
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
//并發隊列
dispatch_queue_tqueue =dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT);
//執行下面的異步函數
dispatch_async(queue, ^{
NSLog(@"==1==%@",[NSThreadcurrentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"==barrier==%@",[NSThreadcurrentThread]);
});
dispatch_async(queue, ^{
NSLog(@"==2==%@",[NSThreadcurrentThread]);
});
}
//延時執行
//在主隊列的delayInSeconds秒后執行任務
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(delayInSeconds *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
//一次性代碼執行
//Block中的代碼保證在程序運行中只執行一次,默認是線程安全的
staticdispatch_once_tonceToken;
dispatch_once(&onceToken, ^{
NSLog(@"onceToken");
});
快速迭代(遍歷)
//全局并發隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//快速遍歷
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"-%zd-%@",index,[NSThread currentThread]);
});
隊列組
//創建隊列組
dispatch_group_t group = dispatch_group_create();
//全局并發隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//執行任務一
dispatch_group_async(group, queue, ^{
NSLog(@"任務一");
});
//執行任務二
dispatch_group_async(group, queue, ^{
NSLog(@"任務一");
});
//任務一和任務二執行完畢后在打印結果
dispatch_group_notify(group, queue, ^{
NSLog(@"結果");
});
說明:將異步執行的任務一和任務二放到隊列組中,兩個完成執行后再執行打印結果的代碼。
資源鏈接鏈接:http://www.lxweimin.com/p/8a7ebecc06f8