1. 程式人生 > IOS開發 >iOS | 面試知識整理 - 多執行緒 (六)

iOS | 面試知識整理 - 多執行緒 (六)

前言:

最近公司專案不怎麼忙,閒暇時間把iOS 在面試中可能會遇到的問題整理了一番,一部分題目是自己面試遇到的,一部分題目則是網上收錄的,方便自己鞏固複習,也分享給大家! 知識點比較多,比較雜,這裡做了分類,下面是分類連結地址;

面試知識點整理 - 目錄:

iOS | 面試知識整理 - OC基礎 (一)
iOS | 面試知識整理 - OC基礎 (二)
iOS | 面試知識整理 - OC基礎 (三)
iOS | 面試知識整理 - UI 相 關 (四)
iOS | 面試知識整理 - 記憶體管理 (五)
iOS | 面試知識整理 - 多 線 程 (六)
iOS | 面試知識整理 - 網路相關 (七)
iOS | 面試知識整理 - 資料持久化 (八)


iOS | 面試知識整理 - Swift 基礎 (九)

iOS | 面試知識整理 - 多 線 程 (六)

1.什麼是多執行緒?

  • 多執行緒是指實現多個執行緒併發執行的技術,進而提升整體處理效能。
  • 同一時間,CPU 只能處理一條執行緒,多執行緒併發執行,其實是 CPU 快速的在多條執行緒之間排程(切換)如果 CPU 排程執行緒的時間足夠快,就造成了多執行緒併發執行的假象
    • 主執行緒的棧區 空間大小為1M,非常非常寶貴
    • 子執行緒的棧區 空間大小為512K記憶體空間
  • 優勢
    充分發揮多核處理器的優勢,將不同執行緒任務分配給不同的處理器,真正進入“平行計算”狀態
  • 弊端
    新執行緒會消耗記憶體控制元件和cpu時間,執行緒太多會降低系統執行效能。

2.程序和執行緒區別?

  • 程序:正在執行的程式,負責程式的記憶體分配,每一個程序都有自己獨立的虛擬記憶體空間。(一個程式執行的動態過程)
  • 執行緒:執行緒是程序中一個獨立執行的路徑(控制單元)一個程序至少包含一條執行緒,即主執行緒可以將耗時的執行路徑(如網路請求)放在其他執行緒中執行。
  • 程序和執行緒的比較
    • 執行緒是 CPU 呼叫的最小單位
    • 程序是 CPU 分配資源和排程的單位
    • 一個程式可以對應多個程序,一個程序中可有多個執行緒,但至少要有一條執行緒,
    • 同一個程序內的執行緒共享程序資源

3.執行緒間怎麼通訊?

  • 執行緒間的通訊體現: 一個執行緒傳遞資料給另一個執行緒,
  • 在一個執行緒中執行完特定的任務後,轉到另一個執行緒繼續執行任務。

4.iOS的多執行緒方案有哪幾種?

5. 什麼是GCD?

GCD(Grand Central Dispatch),又叫做大中央排程,它對執行緒操作進行了封裝,加入了很多新的特性,內部進行了效率優化,提供了簡潔的C語言介面,使用更加高效,也是蘋果推薦的使用方式.

6.GCD 的佇列型別?

GCD的佇列可以分為2大型別

  • 併發佇列(Concurrent Dispatch Queue
    可以讓多個任務併發(同時)執行(自動開啟多個執行緒同時執行任務)
    併發功能只有在非同步(dispatch_async)函式下才有效

  • 序列佇列(Serial Dispatch Queue
    讓任務一個接著一個地執行(一個任務執行完畢後,再執行下一個任務),按照FIFO順序執行.

7.什麼是同步和非同步任務派發(synchronous和asynchronous)?

GCD多執行緒經常會使用 dispatch_syncdispatch_async函式向指定佇列新增任務,分別是同步和非同步

  • 同步指阻塞當前執行緒,既要等待新增的耗時任務塊Block完成後,函式才能返回,後面的程式碼才能繼續執行
  • 非同步指將任務新增到佇列後,函式立即返回,後面的程式碼不用等待新增的任務完成後即可執行,非同步提交無法確定任務執行順序

8.dispatch_after使用?

通過該函式可以讓提交的任務在指定時間後開始執行,也就是延遲執行;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(10 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{
        NSLog(@"10秒後開始執行")
    });
複製程式碼

9.dispatch_group_t (組排程)的使用?

組排程可以實現等待一組操都作完成後執行後續任務.

dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
    //請求1
});
dispatch_group_async(group,^{
    //請求2
});
dispatch_group_async(group,^{
    //請求3
});
dispatch_group_notify(group,^{
    //介面重新整理
    NSLog(@"任務均完成,重新整理介面");
});
複製程式碼

10.dispatch_semaphore (訊號量)如何使用?

  • 用於控制最大併發數
  • 可以防止資源搶奪

與他相關的共有三個函式,分別是

dispatch_semaphore_create,  // 建立最大併發數
dispatch_semaphore_wait。    // -1 開始執行 (0則等待)
dispatch_semaphore_signal,  // +1 
複製程式碼

11.什麼是NSOperation?

NSOperation是基於GCD的上封裝,將執行緒封裝成要執行的操作,不需要管理執行緒的生命週期和同步,比GCD可控性更強

例如:
可以加入操作依賴控制執行順序,設定操作佇列最大併發數,取消操作等

12. NSOperation如何實現操作依賴?

通過任務間新增依賴,可以為任務設定執行的先後順序。接下來通過一個案例來展示設定依賴的效果。

NSOperationQueue *queue=[[NSOperationQueue alloc] init];
//建立操作
NSBlockOperation *operation1=[NSBlockOperation blockOperationWithBlock:^(){
    NSLog(@"執行第1次操作,執行緒:%@",[NSThread currentThread]);
}];
NSBlockOperation *operation2=[NSBlockOperation blockOperationWithBlock:^(){
    NSLog(@"執行第2次操作,執行緒:%@",[NSThread currentThread]);
}];
NSBlockOperation *operation3=[NSBlockOperation blockOperationWithBlock:^(){
    NSLog(@"執行第3次操作,執行緒:%@",[NSThread currentThread]);
}];
//新增依賴
[operation1 addDependency:operation2];
[operation2 addDependency:operation3];
//將操作新增到佇列中去
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
複製程式碼

13.是否可以把比較耗時的操作放在 NSNotification中?

  • 如果在非同步執行緒發的通知,那麼可以執行比較耗時的操作;
  • 如果在主執行緒發的通知,那麼就不可以執行比較耗時的操作

14.說幾個你在工作中使用到的執行緒安全的例子?

  • UIKit(必須在主執行緒)
  • FMDBDataBaseQueue(序列佇列)
  • 等等..

15.dispatch_barrier_(a)sync使用?

  • 一個dispatch barrier 允許在一個併發佇列中建立一個同步點。當在併發佇列中遇到一個barrier,他會延遲執行barrier的block,等待所有在barrier之前提交的blocks執行結束。 這時,barrier block自己開始執行。 之後, 佇列繼續正常的執行操作。

16. dispatch_set_target_queue 使用?

dispatch_set_target_queue(dispatch_object_t object,dispatch_queue_t queue);
複製程式碼

dispatch_set_target_queue 函式有兩個作用:第一,變更佇列的執行優先順序;第二,目標佇列可以成為原佇列的執行階層。

  • 第一個引數是要執行變更的佇列(不能指定主佇列和全域性佇列)
  • 第二個引數是目標佇列(指定全域性佇列)
    主執行緒是相對於什麼而言的

17.在專案什麼時候選擇使用 GCD,什麼時候選 擇 NSOperation?

  • 專案中使用 NSOperation 的優點是 NSOperation 是對執行緒的高度抽象,在專案中使 用它,會使專案的程式結構更好,子類化 NSOperation 的設計思路,是具有面向對 象的優點(複用、封裝),使得實現是多執行緒支援,而介面簡單,建議在複雜專案中 使用。
  • 專案中使用 GCD 的優點是 GCD 本身非常簡單、易用,對於不復雜的多執行緒操 作,會節省程式碼量,而 Block 引數的使用,會是程式碼更為易讀,建議在簡單專案中 使用。

18.說一下 OperationQueue 和 GCD 的區別,以及各自的優勢

  1. GCD是純C語⾔言的API,NSOperationQueue是基於GCD的OC版本封裝
  2. GCD只⽀支援FIFO的佇列列,NSOperationQueue可以很⽅方便便地調整執⾏行行順 序、設 置最⼤大併發數量量
  3. NSOperationQueue可以在輕鬆在Operation間設定依賴關係,⽽而GCD 需要寫很 多的程式碼才能實現
  4. NSOperationQueue⽀支援KVO,可以監測operation是否正在執⾏行行 (isExecuted)、 是否結束(isFinished),是否取消(isCanceld)
  5. GCD的執⾏行行速度⽐比NSOperationQueue快 任務之間不不太互相依賴:GCD 任務之間 有依賴\或者要監聽任務的執⾏行行情況:NSOperationQueue

19.GCD如何取消執行緒?

GCD目前有兩種方式可以取消執行緒:

1.dispatch_block_cancel類似NSOperation一樣,可以取消還未執行的執行緒。但是沒辦法做到取消一個正在執行的執行緒。

dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_block_t block1 = dispatch_block_create(0,^{
    NSLog(@"block1");
});
dispatch_block_t block2 = dispatch_block_create(0,^{
    NSLog(@"block2");
});
    
dispatch_block_t block3 = dispatch_block_create(0,^{
    NSLog(@"block3");
});
    
dispatch_async(queue,block1);
dispatch_async(queue,block2);
dispatch_async(queue,block3);
dispatch_block_cancel(block3); // 取消 block3
複製程式碼

2.使用臨時變數+return 方式取消 正在執行的Block

__block BOOL gcdFlag= NO; // 臨時變數
dispatch_async(dispatch_get_global_queue(0,^{
    for (long i=0; i<1000; i++) {
        NSLog(@"正在執行第i次:%ld",i);
        sleep(1);
        if (gcdFlag==YES) { // 判斷並終止
            NSLog(@"終止");
            return ;
        }
    };
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,^{
                   NSLog(@"我要停止啦");
                   gcdFlag = YES;
               });
複製程式碼

20.NSOperation取消執行緒方式?

1.通過 cancel 取消未執行的單個操作

NSOperationQueue *queue1 = [[NSOperationQueue alloc]init];
NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"block11");
}];
NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"block22");
}];
NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"block33");
}];
[block3 cancel];
[queue1 addOperations:@[block1,block2,block3] waitUntilFinished:YES];
複製程式碼

2.移除佇列裡面所有的操作,但正在執行的操作無法移除

[queue1 cancelAllOperations];
複製程式碼

3.掛起佇列,使佇列任務不再執行,但正在執行的操作無法掛起

queue1.suspended = YES;
複製程式碼

4.我們可以自定義NSOperation,實現取消正在執行的操作。其實就是攔截main方法。

 main方法:
 1、任何操作在執行時,首先會呼叫start方法,start方法會更新操作的狀態(過濾操作,如過濾掉處於“取消”狀態的操作)。
 2、經start方法過濾後,只有正常可執行的操作,就會呼叫main方法。
 3、重寫操作的入口方法(main),就可以在這個方法裡面指定操作執行的任務。
 4、main方法預設是在子執行緒非同步執行的。
複製程式碼

21. 什麼是執行緒安全?

  • 1塊資源可能會被多個執行緒共享,也就是多個執行緒可能會訪問同一塊資源
  • 比如多個執行緒訪問同一個物件、同一個變數、同一個檔案
  • 當多個執行緒訪問同一塊資源時,很容易引發資料錯亂和資料安全問題

22.執行緒安全的處理手段有哪些?

  • 加鎖
  • 同步執行

23.如何理解GCD死鎖?

  • 所謂死鎖.通常是指2個操作相互等待對方完成,造成死迴圈,於是2個操作都無法進行,就產生了死鎖;

24.自旋鎖和互斥鎖的是什麼?

  • 自旋鎖會忙等: 所謂忙等,即在訪問被鎖資源時,呼叫者執行緒不會休眠,而是不停迴圈在那裡,直到被鎖資源釋放鎖。
  • 互斥鎖會休眠: 所謂休眠,即在訪問被鎖資源時,呼叫者執行緒會休眠,此時cpu可以排程其他執行緒工作。直到被鎖資源釋放鎖。此時會喚醒休眠執行緒。

25.OC你瞭解的鎖有哪些?

  • os_unfair_lock ios10 開始
  • OSSpanLock ios10 廢棄
  • dispatch_semaphore 建議使用,效能也比較好
  • dispatch_mutex
  • dispatch_queue 序列
  • NSLock 對 mutex 封裝
  • @synchronized 效能最差

26:自旋和互斥什麼情況下使用?

什麼情況使用自旋鎖比較划算?

  • 預計執行緒等待鎖的時間很短
  • 加鎖的程式碼(臨界區)經常被呼叫,但競爭情況很少發生
  • CPU資源不緊張
  • 多核處理器

什麼情況使用互斥鎖比較划算?

  • 預計執行緒等待鎖的時間較長
  • 單核處理器
  • 臨界區有IO操作
  • 臨界區程式碼複雜或者迴圈量大
  • 臨界區競爭非常激烈

27.程式碼分析一,此函式耗時? 輸出結果

dispatch_queue_t queue = dispatch_queue_create("test",nil);
dispatch_async(queue,^{
    NSLog(@"1");
    sleep(1);
});
dispatch_async(queue,^{
    NSLog(@"2");
    sleep(1);
});
dispatch_sync(queue,^{
    NSLog(@"3");
    sleep(1);
});
此函式耗時?: 3秒
此函式輸出?: 123
複製程式碼
  • 序列佇列非同步執行會開新執行緒,同步執行不會開執行緒,在一個序列隊列了,則是按照順序執行 耗時3秒,列印123;
  • 併發: 任務以FIFO從序列中移除,然後併發執行,可以按照任何順序完成。它會自動開啟多個執行緒同時執行任務
  • 序列: 任務以FIFO從序列中一個一個執行。一次只調度一個任務,佇列中的任務一個接著一個地執行(一個任務執行完畢後,再執行下一個任務)而且只會開啟一條執行緒

28.程式碼分析二,列印結果

dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_async(queue,^{
    NSLog(@"1");
    [self performSelector:@selector(test) withObject:nil afterDelay:0];
    NSLog(@"3");
});

- (void)test{
    NSLog(@"2");
}
複製程式碼

列印 1,3
performSelector after 是基於 timer 定製器,定時器又是基於 runloop 實現的;任務2在子執行緒中,子執行緒預設 runloop 是不開啟的,所以不執行2

29.請問下面程式碼的列印結果是什麼?

- (void)test{
    NSLog(@"2");
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSThread *thread = [[NSThread alloc]initWithBlock:^{
        NSLog(@"1");
    }];
    [thread start];
    [self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
}
複製程式碼

列印1

  • start 執行完,執行緒就銷燬了.任務 test 沒法執行了

下一篇入口:

iOS | 面試知識整理 - 網路相關 (七)

其實呢作為一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是我的微信 大家有興趣可以新增 邀請小夥伴們進入微信群裡一起 交流(想要加群的可以加小編微信進群哦17512010526)


作者:LEON_iOS
連結:www.jianshu.com/p/a937fd308…