iOS 多線程總結

更多實用詳見 Demo
本文導讀

一些相關總結(同步、異步、並行、串行概念,GCD、NSOperation對比)
1、進程與線程概念
2、多線程-GCD
3、多線程-NSOperation
4、多線程-NSThread
5、隊列概念
6、多線程面試題java

同步、異步、並行、串行
  1. 同步和異步決定了要不要開啓行的新的線程。同步在當前線程中執行任務,不具有開啓新線程的能力。異步在新的線程中執行任務,具有開啓新線程的能力。
  2. 並行和串行決定了任務的執行方式。併發多個任務併發(同時)執行。串行,一個任務執行完畢後,再執行下一個任務。
GCD、NSOperation對比:
  • GCD --> iOS 4.0
    (1)將任務(block)添加到隊列(串行/併發(全局)),指定執行任務的方法(同步(阻塞)/異步)
    (2)拿到 dispatch_get_main_queue()。 線程間通訊
    (3)NSOperation沒法作到一次性執行、延遲執行、調度組(op相對複雜)
    (4)提供了更多的控制能力以及操做隊列中所不能使用的底層函數ios

  • NSOperation ----> iOS 2.0 (後來蘋果改造了NSOperation的底層)
    (1)將操做(異步執行)添加到隊列(併發/全局)
    (2)[NSOperationQueue mainQueue] 主隊列。 任務添加到主隊列, 就會在主線程執行
    (3)提供了一些GCD很差實現的,」最大併發數「、依賴關係、暫停/繼續 --- 掛起、取消全部的任務git

  • 對比:
    (1)GCD是純C語言的API,NSOperationQueue是基於GCD的OC版本封裝
    (2)GCD只支持FIFO的隊列,NSOperationQueue能夠很方便的調整執行順序、設置最大併發數量 (FIFO 就是先進先出)
    (3)NSOperationQueue能夠很輕鬆的在Operation間設置依賴關係,而GCD須要寫不少代碼才能實現
    (4)NSOperationQueue支持KVO(鍵值觀察者),能夠監聽operation是否正在執行 (isExecuted)、是否結束(isFinished)、是否取消(isCanceld)
    (5)GCD的執行速度比NSOperationQueue快(封裝GCD,更高層的東西,性能很差,由於還要轉化成GCD)快程序員

  • 在項目何時選擇使用GCD,何時選擇NSOperation?
    · NSOperation:
    任務之間有依賴\或者要監放任務的執行狀況
    項目中使用的優勢是它是對線程的高度抽象,會使程序結構更好,子類化NSOperation的設計思路具備面向對象的優勢(複用、封裝),使得實現多線程支持,而接口簡單,建議在複雜項目中使用。
    · GCD:
    任務之間不太依賴 項目中使用GCD的優勢是GCD自己很是簡單、易用,對於不復雜的多線程操做,會節省代碼量,而Block參數的使用,會是代碼更爲易讀,建議在簡單項目中使用。github

1、進程與線程概念:

  • 進程百度百科面試

    進程(Process)是計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操做系統結構的基礎。在早期面向進程設計的計算機結構中,進程是程序的基本執行實體;在當代面向線程設計的計算機結構中,進程是線程的容器。程序是指令、數據及其組織形式的描述,進程是程序的實體。objective-c

  • 線程百度百科數據庫

    線程,有時被稱爲輕量進程(Lightweight Process,LWP),是程序執行流的最小單元。一個標準的線程由線程ID,當前指令指針(PC),寄存器集合和堆棧組成。另外,線程是進程中的一個實體,是被系統獨立調度和分派的基本單位,線程本身不擁有系統資源,只擁有一點兒在運行中必不可少的資源,但它可與同屬一個進程的其它線程共享進程所擁有的所有資源。一個線程能夠建立和撤消另外一個線程,同一進程中的多個線程之間能夠併發執行。因爲線程之間的相互制約,導致線程在運行中呈現出間斷性。線程也有就緒阻塞運行三種基本狀態。就緒狀態是指線程具有運行的全部條件,邏輯上能夠運行,在等待處理機;運行狀態是指線程佔有處理機正在運行;阻塞狀態是指線程在等待一個事件(如某個信號量),邏輯上不可執行。每個程序都至少有一個線程,若程序只有一個線程,那就是程序自己。 線程是程序中一個單一的順序控制流程。進程內有一個相對獨立的、可調度的執行單元,是系統獨立調度和分派CPU的基本單位指令運行時的程序的調度單位。在單個程序中同時運行多個線程完成不一樣的工做,稱爲多線程編程

  • 進程總結c#

    1. 進程是指在系統中正在運行的一個應用程序
    2. 每一個進程之間是獨立的,每一個進程均運行在其專用且受保護的內存空間內
  • 線程總結

    1. 1個進程要想執行任務,必須得有線程(每1個進程至少要有一個線程
    2. 線程是進程的基本執行單元,一個進程的全部任務都在線程中執行

    進程(process)是一塊包含了某些資源的內存區域。操做系統利用進程把它的工做劃分爲一些功能單元。進程中所包含的一個或多個執行單元稱爲線程(thread)。進程還擁有一個私有的虛擬地址空間,該空間僅能被它所包含的線程訪問。一般在一個進程中能夠包含若干個線程,它們能夠利用進程所擁有的資源。在引入線程的操做系統中,一般都是把進程做爲分配資源的基本單位,而把線程做爲獨立運行和獨立調度的基本單位。因爲線程比進程更小,基本上不擁有系統資源,故對它的調度所付出的開銷就會小得多,能更高效的提升系統內多個程序間併發執行的程度。簡而言之,一個程序至少有一個進程,一個進程至少有一個線程。一個程序就是一個進程,而一個程序中的多個任務則被稱爲線程。 線程只能歸屬於一個進程而且它只能訪問該進程所擁有的資源。當操做系統建立一個進程後,該進程會自動申請一個名爲主線程或首要線程的線程。應用程序(application)是由一個或多個相互協做的進程組成的。另外,進程在執行過程當中擁有獨立的內存單元,而多個線程共享內存,從而極大地提升了程序的運行效率。線程在執行過程當中與進程仍是有區別的。每一個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口。可是線程不可以獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。 從邏輯角度來看,多線程的意義在於一個應用程序中,有多個執行部分能夠同時執行。但操做系統並無將多個線程看作多個獨立的應用,來實現進程的調度和管理以及資源分配。這就是進程和線程的重要區別。 進程是具備必定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。 線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。線程本身基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),可是它可與同屬一個進程的其餘的線程共享進程所擁有的所有資源。

  • 線程與進程對比?

    1. 調度:線程做爲調度和分配的基本單位,進程做爲擁有資源的基本單位
    2. 併發性:不只進程之間能夠併發執行,同一個進程的多個線程之間也可併發執行
    3. 擁有資源:進程是擁有資源的一個獨立單位,線程不擁有系統資源,但能夠訪問隸屬於進程的資源
    4. 系統開銷: 進程切換(建立或撤消)時,因爲系統都要爲之分配和回收資源,致使系統的開銷明顯大於建立或撤消線程時的開銷,效率要差一些。對於一些要求同時進行而且又要共享某些變量的併發操做,只能用線程,不能用進程
    5. 進程都有本身的獨立地址空間。一個進程崩潰後,在保護模式下不會對其它進程產生影響。線程有本身的堆、局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,因此多進程的程序要比多線程的程序健壯
    6. 線程是指進程內的一個執行單元,也是進程內的可調度實體。它能夠與同進程的其餘線程共享數據,但擁有本身的棧空間

    簡而言之,一個程序至少有一個進程,一個進程至少有一個線程(即主線程)。一個程序就是一個進程,而一個程序中的多個任務則被稱爲線程。

  • 學習多線程,先了解手機裏有幾個重要的芯片:
    主芯片:CPU(雙核)+GPU(至關於電腦裏的顯卡)
    內存(RAM):至關於電腦的內存條
    閃存芯片:相於於電腦的硬盤
    電源管理芯片
    藍牙、wifi、gps芯片

  • 多線程應用:
    耗時操做(數據庫中的讀取,圖片的處理(濾鏡) )
    進程是來幫你分配內存的
    多線程開線程通常五條之內

2、多線程-GCD

GCD簡介

全稱Grand Central Dispatch,純C語言,提供了很是多強大的函數

  • 優點:

    1. 蘋果公司爲多核的並行運算提出的解決方案
    2. GCD會自動利用更多的CPU內核(好比雙核、四核)
    3. GCD會自動管理線程的生命週期(建立線程、調度任務、銷燬線程)
    4. 程序員只須要告訴GCD想要執行什麼任務,不須要編寫任何線程管理代碼
  • GCD中2個核心概念:

    1. 任務:執行什麼操做
    2. 隊列:用來存聽任務
  • GCD的使用就兩個步驟:

    1. 定製任務:肯定想要作的事
    2. 將任務添加到隊列中:GCD會自動將隊列中的任務取出,放到對應的線程中執行。任務的取出遵循隊列的FIFO原則(先進先出,後進後出)
  • 隊列的類型: GCD的隊列能夠分爲兩種類型:
    · 併發隊列
    (1) 隊列中的任務 一般 會併發執行
    (2) 可讓多個任務併發(同時)執行(自動開啓多個線程同時執行任務)
    (3) 併發功能只有在異步(dispatch_async)函數下才有效
    · 串行隊列:
    (1) 隊列中的任務 會順序執行
    (2) 讓任務一個接一個的執行(一個任務執行完畢後,再執行下一個任務)

  • GCD內部是怎麼實現的? (1) iOS和OS X的核心是XNU內核,GCD是基於XNU內核實現的
    (2) GCD的API所有在libdispatch庫中
    (3) GCD底層實現主要有Dispatch Queue和Dispatch Source · Dispatch Queue: 管理block(操做) · Dispatch Source :處理事件(MACH端口發送,MACH端口接收,檢測與進程相關事件等10種事件)

基本使用
  1. 全局隊列與並行隊列的區別
    (1) 不須要建立,直接GET就能用
    (2) 兩個隊列的執行效果相同
    (3) 全局隊列沒有名稱,調試時,沒法確認準確隊列
    (4) 全局隊列有高中默認優先級
  2. 全局隊列 dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    3. 並行隊列 dispatch_queue_t q = dispatch_queue_create("queuename", DISPATCH_QUEUE_CONCURRENT);
    4. 串行隊列 dispatch_queue_t t = dispatch_queue_create("queuename",DISPATCH_QUEUE_SERIAL);
GCD高級:調度組dispatch_group
  • 應用場景: 開發的時候,有的時候出現多個網絡請求都完成之後(每個網絡請求的事件長短不必定),再統一通知用戶。好比: 下載小說:三國演義, 紅樓夢, 水滸
    // 實例化一個調度組
      dispatch_group_t group = dispatch_group_create();
      
      // 隊列
      dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
      
      // 任務添加到隊列queue
      dispatch_group_async(group, queue, ^{
          NSLog(@"下載小說A---%@", [NSThread currentThread]);
      });
      
      dispatch_group_async(group, queue, ^{
          NSLog(@"下載小說B---%@", [NSThread currentThread]);
      });
      
      dispatch_group_async(group, queue, ^{
          NSLog(@"下載小說X---%@", [NSThread currentThread]);
      });
      
      // 得到全部調度組裏面的異步任務完成的通知
      //    dispatch_group_notify(group, queue, ^{
      //        NSLog(@"下載完成,請觀看%@", [NSThread currentThread]); // 異步的
      //    });
      
      //注意點: 在調度組完成通知裏,能夠跨隊列通訊
      dispatch_group_notify(group, dispatch_get_main_queue(), ^{
          // 更新UI,在主線程
          NSLog(@"下載完成,請觀看%@", [NSThread currentThread]); // 異步的
      });
    複製代碼
GCD高級:dispatch_group_enter、dispatch_group_leve

dispatch_group_enter : 通知 group,下個任務要放入 group 中執行了 dispatch_group_leave: 通知 group,任務成功完成,要移除,與 enter成對出現 dispatch_group_wait: 在任務組完成時調用,或者任務組超時是調用(完成指的是enter和leave次數同樣多)
dispatch_group_notify: 只要任務所有完成了,就會在最後調用

- (void)gcdGroupEnterAndLeave {
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 請求1
        [網絡請求:{
            成功:dispatch_group_leave(group);
            失敗:dispatch_group_leave(group);
        }];
    });
    dispatch_group_enter;
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 請求2
        [網絡請求:{
            成功:dispatch_group_leave;
            失敗:dispatch_group_leave;
        }];
    });
    dispatch_group_enter(group);
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 請求3
        [網絡請求:{
            成功:dispatch_group_leave(group);
            失敗:dispatch_group_leave(group);
        }];
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 界面刷新
        NSLog(@"任務均完成,刷新界面");
    });
}
複製代碼
GCD高級:柵欄函數dispatch_barrier_(a)sync

首先是 Apple Documentation 對dispatch_barrier_async的解釋

  • dispatch barrier容許開發者在一個並行隊列中創造一個同步點,其實也是用於在競爭條件下資源的保護,防止同一資源同時被使用。 在並行異步任務中: barrier_sync前的任務併發執行,syncbarrier後的任務必須等syncbarrier中的任務執行完成以後纔會執行他們,也會阻塞主線程的任務 barrier_async前的任務併發執行,barrier_async後的任務必須等barrier_async中的任務執行完成以後纔會執行他們,可是Barrier不能阻塞主線程的任務

  • 做用如:打造線程安全的 NSMutableArray NSMutableArray自己是線程不安全的。簡單來講,線程安全就是多個線程訪問同一段代碼,程序不會異常、不Crash。而編寫線程安全的代碼主要依靠線程同步。 一、不使用atomic修飾屬性打造線程安全的緣由: 1 ) atomic 的內存管理語義是原子性的,僅保證了屬性的setter和getter方法是原子性的,是線程安全的,可是屬性的其餘方法,如數組添加/移除元素等並非原子操做,因此不能保證屬性是線程安全的。 2 ) atomic雖然保證了getter、setter方法線程安全,可是付出的代價很大,執行效率要比nonatomic慢不少倍(有說法是慢10-20倍)。 總之:使用nonatomic修飾NSMutableArray對象就能夠了,而使用鎖、dispatch_queue來保證NSMutableArray對象的線程安全。 二、打造線程安全的NSMutableArray 在《Effective Objective-C 2.0..》書中第41條:多用派發隊列,少用同步鎖中指出:使用「串行同步隊列」(serial synchronization queue),將讀取操做及寫入操做都安排在同一個隊列裏,便可保證數據同步。而經過併發隊列,結合GCD的柵欄塊(barrier)來不只實現數據同步線程安全,還比串行同步隊列方式更高效。

幾種任務嵌套:
  • 在主隊列開啓異步任務,不會開啓新的線程而是依然在主線程中執行代碼塊中的代碼。爲何不會阻塞線程?  主隊列開啓異步任務,雖然不會開啓新的線程,可是他會把異步任務下降優先級,等閒着的時候,就會在主線程上執行異步任務。

  • 在主隊列開啓同步任務,爲何會阻塞線程?  在主隊列開啓同步任務,由於主隊列是串行隊列,裏面的線程是有順序的,先執行完一個線程才執行下一個線程,而主隊列始終就只有一個主線程,主線程是不會執行完畢的,由於他是無限循環的,除非關閉應用程序。所以在主線程開啓一個同步任務,同步任務會想搶佔執行的資源,而主線程任務一直在執行某些操做,不願放手。兩個的優先級都很高,最終致使死鎖,阻塞線程了。

  • 主線程隊列注意: 下面代碼執行順序 1111 2222

- (void)main_queue_deadlock {
    dispatch_queue_t q = dispatch_get_main_queue();
    NSLog(@"1111");
    dispatch_async(q, ^{
        NSLog(@"主隊列異步 %@", [NSThread currentThread]);
    });
    NSLog(@"2222");
    // 下面會形成線程死鎖
//    dispatch_sync(q, ^{
//        NSLog(@"主隊列同步 %@", [NSThread currentThread]);
//    });
}
複製代碼
  • 串行隊列開啓異步任務後嵌套同步任務形成死鎖
dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
dispatch_async(q, ^{
    NSLog(@"異步任務 %@", [NSThread currentThread]);
    // 下面開啓同步形成死鎖:由於串行隊列中線程是有執行順序的,須要等上面開啓的異步任務執行完畢,纔會執行下面開啓的同步任務。而上面的異步任務還沒執行完,要到下面的大括號纔算執行完畢,而下面的同步任務已經在搶佔資源了,就會發生死鎖。
    dispatch_sync(q, ^{
        NSLog(@"同步任務 %@", [NSThread currentThread]);
    });
});
複製代碼
  • 串行隊列開啓同步任務後嵌套同步任務形成死鎖
dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
dispatch_sync(q, ^{
  NSLog(@"同步任務 %@", [NSThread currentThread]);
  // 下面開啓同步形成死鎖:由於串行隊列中線程是有執行順序的,須要等上面開啓的同步任務執行完畢,纔會執行下面開啓的同步任務。而上面的同步任務還沒執行完,要到下面的大括號纔算執行完畢,而下面的同步任務已經在搶佔資源了,就會發生死鎖。
  dispatch_sync(q, ^{
	 NSLog(@"同步任務 %@", [NSThread currentThread]);
  });
});
NSLog(@"同步任務 %@", [NSThread currentThread]);
複製代碼
  • 串行隊列開啓同步任務後嵌套異步任務不形成死鎖
  • 並行隊列的任務嵌套例子
dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    // 任務嵌套
    dispatch_sync(q, ^{
        NSLog(@"1 %@", [NSThread currentThread]);
        dispatch_sync(q, ^{
            NSLog(@"2 %@", [NSThread currentThread]);
            dispatch_sync(q, ^{
                NSLog(@"3 %@", [NSThread currentThread]);
            });
        });
        dispatch_async(q, ^{
            NSLog(@"4 %@", [NSThread currentThread]);
        });
        NSLog(@"5 %@", [NSThread currentThread]);
    });
// 運行結果是: 12345 或12354  
複製代碼

並行隊列裏開啓同步任務是有執行順序的,只有異步纔沒有順序
串行隊列開啓異步任務,是有順序的

3、多線程-NSOperation:

  • 添加依賴
// 建立隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 建立3個操做
    NSOperation *a = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operationA---");
    }];
    NSOperation *b = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operationB---");
    }];
    NSOperation *c = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operationC---");
    }];
    
    // 添加依賴
    [c addDependency:a];
    [c addDependency:b];
    
    // 執行操做
    [queue addOperation:a];
    [queue addOperation:b];
    [queue addOperation:c];
複製代碼
NSOperation queue

存放NSOperation的集合類。
(1) 用來存放NSOperation對象的隊列,能夠用來異步執行一些操做
(2) 通常能夠用在網絡請求等耗時操做
解釋:操做和操做隊列,基本能夠當作java中的線程和線程池的概念。用於處理ios多線程開發的問題。網上部分資料提到一點是,雖然是queue,可是卻並非帶有隊列的概念,放入的操做並不是是按照嚴格的先進現出。
這邊又有個疑點是,對於隊列來講,先進先出的概念是Afunc添加進隊列,Bfunc緊跟着也進入隊列,Afunc先執行這個是必然的,可是Bfunc是等Afunc徹底操做完之後,B纔開始啓動而且執行,所以隊列的概念離亂上有點違背了多線程處理這個概念。可是轉念一想其實能夠參考銀行的取票和叫號系統。所以對於A比B先排隊取票可是B率先執行完操做,咱們亦然能夠感性認爲這仍是一個隊列。可是後來看到一票關於這操做隊列話題的文章,其中有一句提到「由於兩個操做提交的時間間隔很近,線程池中的線程,誰先啓動是不定的。」瞬間以爲這個queue名字有點忽悠人了,還不如pool~綜合一點,咱們知道他能夠比較大的用處在於能夠幫組多線程編程就行了。

4、多線程-NSThread

  • 建立線程的方式
// 建立線程的方式1
- (void)createThread1 {
    // 建立線程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download:) object:@"http://a.png"];
    thread.name = @"下載線程";
    
    // 啓動線程(調用self的download方法)
    [thread start];
}
// 建立線程的方式2
- (void)createThread2 {
    [NSThread detachNewThreadSelector:@selector(download:) toTarget:self withObject:@"http://b.jpg"];
}
// 建立線程的方式3
- (void)createThread3 {
    // 這2個不會建立線程,在當前線程中執行
    //    [self performSelector:@selector(download:) withObject:@"http://c.gif"];
    //    [self download:@"http://c.gif"];
    
    [self performSelectorInBackground:@selector(download:) withObject:@"http://c.gif"];
}
複製代碼
  • 線程安全
-(void)threadSafe {
    self.leftTicketCount = 50;
    
    self.thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread1.name = @"1號窗口";
    
    self.thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread2.name = @"2號窗口";
    
    self.thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread3.name = @"3號窗口";
    
    [self threadSafeStart];
}
- (void)threadSafeStart {
    [self.thread1 start];
    [self.thread2 start];
    [self.thread3 start];
}
// 賣票
- (void)saleTicket {
    while (1) {
        // ()小括號裏面放的是鎖對象
        @synchronized(self) { // 開始加鎖
            int count = self.leftTicketCount;
            if (count > 0) {
                [NSThread sleepForTimeInterval:0.05];
                
                self.leftTicketCount = count - 1;
                
                NSLog(@"%@賣了一張票, 剩餘%d張票", [NSThread currentThread].name, self.leftTicketCount);
            } else {
                return; // 退出循環
            }
        } // 解鎖
    }
}
複製代碼

5、隊列概念:

  • 主線程 每個應用程序都只有一個主線程。
    全部UI的更新工做,都必須在主線程上執行! 主線程是一直工做的,並且除非將程序殺掉,不然主線程的工做永遠不會結束!

  • 隊列和線程的區別:
    隊列是管理線程的,至關於線程池,能管理線程何時執行。

  • 隊列分爲串行隊列和並行隊列:
    串行隊列:隊列中的線程按順序執行(不會同時執行)
    並行隊列:隊列中的線程會併發執行,可能會有一個疑問,隊列不是先進先出嗎,若是後面的任務執行完了,怎麼出去的了。這裏須要強調下,任務執行完畢了,不必定出隊列。只有前面的任務執行完了,纔會出隊列,也就是說你即便執行完畢了,也必須等前面的任務執行完畢出隊列,才能夠出去。

  • 主線程和工做線程的區別:
    主線程: 系統自動建立 能直操做UI 不能作耗時比較多的操做 自帶自動釋放池 RunLoop自動運行
    工做線程:程序猿手工建立 不能直接操做UI 寫死循環都行 須要手工寫自動釋放池 手動運行

  • 主線程隊列和GCD建立的隊列區別: 主線程隊列:主線程隊列中不能開啓同步,會阻塞主線程。只能開啓異步任務,開啓異步任務也不會開啓新的線程,只是下降異步任務的優先級,讓cpu空閒的時候纔去調用。而同步任務,會搶佔主線程的資源,會形成死鎖。 GCD建立的隊列:在GCD中建立的隊列優先級沒有主隊列高,因此在GCD中的串行隊列開啓同步任務裏面沒有嵌套任務是不會阻塞主線程,只有一種可能致使死鎖,就是串行隊列裏,嵌套開啓任務,有可能會致使死鎖。

  • 同步與異步的區別:  同步任務優先級高,在線程中有執行順序,不會開啓新的線程。 異步任務優先級低,在線程中執行沒有順序,看cpu閒不閒。在主隊列中不會開啓新的線程,其餘隊列會開啓新的線程。

6、多線程面試題:

  • 回到主線程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 下載圖片
    UIImage *image = nil;
    dispatch_async(dispatch_get_main_queue(), ^{
        // 回到主線程
    });
    // [self performSelector:@selector(settingImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES modes:nil];
    // [self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:YES];
});
複製代碼
  • 子線程的定時器: 代碼片斷原有的目的是異步延遲0.3秒後輸出Hello world。可是運行以後發現不會輸出Hello world。 緣由是非主線程的NSRunLoop默認沒有開啓,而- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay; 函數內部是經過NSTimer定時器實現,在NSRunLoop沒有開啓的狀況下,NSTimer不會獲得正常運行。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
     [self performSelector:@selector(fireBlock:) withObject:^{  
        NSLog(@"hello world");  
     } afterDelay:0.3];  
 });  
複製代碼
  • 主線程中執行代碼
[self performSelectorOnMainThread: withObject: waitUntilDone:];
[self performSelector: onThread:[NSThread mainThread] withObject: waitUntilDone:];
dispatch_async(dispatch_get_main_queue(), ^{
});
複製代碼
  • 延時執行
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW,  (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){        
});
[self performSelector: withObject: afterDelay:];
[NSTimer scheduledTimerWithTimeInterval: target: selector: userInfo: repeats:];
複製代碼
  • GCD怎麼用的? 全局隊列異步操做,會新建多個子線程,操做無序執行,若是隊列前有其餘任務,會等待其餘任務執行完畢在調用; 全局隊列同步操做,不會新建線程,順序執行; 主隊列全部的操做都是主線程順序執行,沒有異步概念,主隊列添加的同步操做永遠不會執行,會死鎖; 串行隊列添加的同步操做會死鎖,可是會執行嵌套同步操做以前的代碼; 並行隊列添加的同步操做不會死鎖都在主線程執行; 全局隊列添加的同步操做不會死鎖; 同步操做 最主要的目的,阻塞並行隊列任務的執行,只有當前的同步任務執行完畢以後,後邊的任務纔會執行,應用:用戶登陸。

  • 多線程安全怎麼控制:

    1. 只在主線程刷新訪問UI
    2. 若是要防止資源搶奪,得用synchronized進行加鎖保護
    3. 若是異步操做要保證線程安全等問題,儘可能使用GCD(有些函數默認就是安全的)
  • @synchronized @synchronized 的做用是建立一個互斥鎖,保證此時沒有其它線程對self對象進行修改。這個是objective-c的一個鎖定令牌,防止self對象在同一時間內被其它線程訪問,起到線程的保護做用。 通常在公用變量的時候使用,如單例模式或者操做類的static變量中使用。

  • NSLock 從微觀上看一個線程在CPU上走走停停的,其運行狀態能夠分爲(經典)三種:運行狀態、就緒狀態、阻塞。 兩個線程同時訪問一個共享的變量,一個對其加一、一個對其減1 因爲內存的速度遠低於CPU的速度,因此在設計CPU時一般不容許直接對內存中的數據進行運行。若是要運算內存中的數據一般是用一條CPU指令把內存中的數據讀入CPU、再用另一CPU指令對CPU中的數據作運算、最後再用一條CPU指令把結果寫回內存。 i++ 讀數據 運算 寫數據 因爲CPU在執行兩條指令中間能夠被打斷,就可能致使另外一個訪問一樣內存的線程運行,最終致使運算結果出錯 解決辦法就是加線程鎖 NSLock lock方法 加鎖:若是這個沒被鎖上則直接上鎖、若是鎖已經被其它線程鎖上了當前線程就阻塞直至這個鎖被其它線程解鎖 unlock方法 解鎖:解開這個鎖,若是有其它線程由於等這個鎖而進入了阻塞狀態還要把那個線程變成就緒 用lock保護共享數據的原理 先上鎖 訪問共享的數據(臨界區) 解鎖

  • NSCondition 線程的同步,有一個經典的生產者消費者問題: 好比一個線程A負責下載數據 另外一個線程B負責處理數據 還有一個線程C負責顯示 解決方法就是用NSCondition: lock wait signal unlock 產生者線程(產生數據的線程),生產數據以後: lock signal(發出信號:若是有其它線程在等待這個信號就把那個線程變爲就緒狀態) unlock 消費者線程(處理或使用數據的線程): lock wait(等待信號:若是有線程發信號當前線程就會進入阻塞狀態、直到有線程發出信號) unlock NSCondition有一個問題:假喚醒(wait的時候明明沒有線程發信號,wait也可能返回),一般用一個表示狀態的變量就能解決這個問題

  • 不可改變的對象,一般是線程安全的 線程安全的類和函數: NSArray, NSData, NSNumber..... 非線程安全: NSBundle, NSCoder, NSArchiver, NSMutableArray

  • 列舉幾種進程的同步機制,並比較其優缺點。 答案:原子操做、信號量機制、自旋鎖、管程、會合、分佈式系統

  • 進程之間通訊的途徑 答案:共享存儲系統消息傳遞系統管道:以文件系統爲基礎

  • 進程死鎖的緣由 答案:資源競爭及進程推動順序非法

  • 死鎖的4個必要條件 答案:互斥、請求保持、不可剝奪、環路

  • 死鎖的處理 答案:鴕鳥策略、預防策略、避免策略、檢測與解除死鎖

相關文章
相關標籤/搜索