iOS底層原理之GCD的任務與隊列

GCD

簡介

Grand Central Dispatch(GCD)是 Apple 開發的一個多核編程的解決方法。它是一套純 C 語言的 API,主要用於優化應用程序以支持多核處理器以及其餘對稱多處理系統。它是一個在線程池模式的基礎上執行的併發任務。編程

GCD的優勢

  • GCD支持多核並行計算
  • GCD自動管理線程的生命週期(線程的建立、調起、等待、銷燬)
  • 使用者只需告知GCD執行任務

任務與隊列

任務

任務即執行操做,就是線程中須要執行的代碼。在GCD中就是放在block中執行的代碼都是任務。任務分爲同步任務(sync)異步任務(async)markdown

  • 同步任務(sync):同步添加到隊列當中,當前面的任務沒有執行完畢時會一直等待,直到前面的任務執行完畢纔會執行當前加入的任務。不能夠在新的線程中執行,不具有開闢新的線程能力。
  • 異步任務(async):異步添加到隊列當中,無需等待前面的任務執行完成才執行,加入隊列就會執行。能夠在新的線程中執行,具有開闢新的線程能力。注意:具有開啓新線程的能力不表明必定開啓新的線程

隊列

指執行任務的隊列,就是等待執行的任務列表,遵循先進先出IFFO原則。在GCD中有串行隊列(Serial Dispatch Queue)並行隊列(Concurrent Dispatch Queue),又叫併發隊列併發

  • 串行隊列(Serial Dispatch Queue):只開啓一個線程,每次只有一個任務執行,任務執行完成後纔會執行下一個任務。
  • 並行隊列(Concurrent Dispatch Queue):可讓多個任務同時執行,能夠開啓多個線程來執行多個任務。注意:併發隊列只有在異步函數下才有效

特殊隊列

  • 串行主隊列:一種串行的主隊列,在主隊列中調用不能在主線程中再次調用同步任務,不然會形成堵塞,可是能夠在其餘線程調用同步任務
  • 全局併發隊列:一種全局的併發隊列

隊列與任務的建立

建立隊列

  • 建立串行隊列
//第一個參數是隊列的名字,自定義;第二個參數表示是串行仍是併發
//DISPATCH_QUEUE_SERIAL = NULL,所以DISPATCH_QUEUE_SERIAL能夠用NULL代替建立串行隊列
dispatch_queue_t queue = dispatch_queue_create("shifx", DISPATCH_QUEUE_SERIAL);
複製代碼
  • 建立併發隊列
//第一個參數是隊列的名字,自定義;第二個參數表示是串行仍是併發
dispatch_queue_t queue = dispatch_queue_create("shifx", DISPATCH_QUEUE_CONCURRENT);
複製代碼

任務的建立

  • 建立同步任務
//建立同步任務(queue是隊列)
dispatch_sync(queue, ^{
        
});
複製代碼
  • 建立異步任務
//建立異步任務(queue是隊列)
dispatch_async(queue, ^{
        
});
複製代碼

隊列與任務總結

綜上所述,咱們能夠得出一共四種組合外加兩種特殊的隊列,即異步

  • 串行隊列+同步執行
  • 併發隊列+同步執行
  • 串行隊列+異步執行
  • 併發隊列+異步執行
  • 串行主隊列
  • 全局併發隊列

隊列與任務組合使用

串行隊列+同步執行

代碼實現

-(void)serialSysncText {
    //串行隊列
    dispatch_queue_t serial = dispatch_queue_create("shifx", DISPATCH_QUEUE_SERIAL);
    NSLog(@"串行同步任務開始");
    dispatch_sync(serial, ^{
        sleep(1);
        for (int i = 0; i<3; i++) {
            NSLog(@"串行同步任務1 = %d",i);
        }
    });
    dispatch_sync(serial, ^{
        sleep(1);
        for (int i = 0; i<3; i++) {
            NSLog(@"串行同步任務2 = %d",i);
        }
    });
    NSLog(@"串行同步任務結束");
}
複製代碼

運行結果

從運行結果當中,咱們能夠看到串行隊列中的同步任務都是按照前後順序依次執行的。async

併發隊列+同步任務

代碼實現

-(void)concurrentAsysncText {
    //併發隊列
    dispatch_queue_t concurrent = dispatch_queue_create("shifx", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"併發同步任務開始");
    dispatch_sync(concurrent, ^{
        sleep(1);
        for (int i = 0; i<3; i++) {
            NSLog(@"併發同步任務1 = %d",i);
        }
    });
    dispatch_sync(concurrent, ^{
        sleep(1);
        for (int i = 0; i<3; i++) {
            NSLog(@"併發同步任務2 = %d",i);
        }
    });
    NSLog(@"併發同步任務結束");
}
複製代碼

運行結果

從結果得知,併發隊列執行的同步任務也是按照順序依次執行的,由於它們都在主線程中執行的,沒有開啓新的線程。感興趣的小夥伴能夠打印下線程看下。函數

串行隊列+異步執行

代碼實現

-(void)serialAsysncText {
    //串行隊列
    dispatch_queue_t serial = dispatch_queue_create("shifx", DISPATCH_QUEUE_SERIAL);
    NSLog(@"串行異步任務開始");
    dispatch_async(serial, ^{
        
        for (int i = 0; i<3; i++) {
            sleep(1);
            NSLog(@"串行異步任務1 = %d",i);
            NSLog(@"當前線程:%@",[NSThread currentThread]);
        }
    });
    dispatch_async(serial, ^{
        for (int i = 0; i<3; i++) {
            sleep(1);
            NSLog(@"串行異步任務2 = %d",i);
            NSLog(@"當前線程:%@",[NSThread currentThread]);
        }
    });
    NSLog(@"串行異步任務結束");
}
複製代碼

運行結果

從運行結果能夠看出,優化

  • 全部的異步任務都是在打印串行異步任務結束後開始執行;
  • 開啓了新的線程,但只開啓了一個線程;
  • 在串行隊列中的異步任務也是按照前後順序執行。

併發隊列+異步執行

代碼實現

-(void)concurrentAsysncText {
    //併發隊列
    dispatch_queue_t concurrent = dispatch_queue_create("shifx", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"併發異步任務開始");
    dispatch_async(concurrent, ^{
        for (int i = 0; i<3; i++) {
            sleep(1);
            NSLog(@"併發異步任務1 = %d",i);
            NSLog(@"當前線程:%@",[NSThread currentThread]);
        }
    });
    dispatch_async(concurrent, ^{
        
        for (int i = 0; i<3; i++) {
            sleep(1);
            NSLog(@"併發異步任務2 = %d",i);
            NSLog(@"當前線程:%@",[NSThread currentThread]);
        }
    });
    NSLog(@"併發異步任務結束");
}
複製代碼

運行結果

從運行結果來看,spa

  • 全部的異步任務都是在打印併發異步任務結束後開始執行;
  • 因爲兩個異步任務,併發隊列開啓了兩個新的線程
  • 兩個異步任務執行沒有前後順序

主隊列 + 同步任務

在主線程調用主隊列同步任務

因爲主隊列是串行隊列,它不能在主線程中調用主隊列同步任務,以下所示崩潰緣由就是同步任務放在了主線程中,須要等待主線程執行完畢才能夠,而主線程須要等待該方法執行完成,二者互相等待形成死鎖,因此崩潰了。線程

其餘線程調用主隊列同步任務

代碼3d

//使用 NSThread 的 detachNewThreadWithBlock 方法會建立線程
[NSThread detachNewThreadWithBlock:^{
    [self mainSysncText];
}];
    
-(void)mainSysncText {
    NSLog(@"當前所在的線程:%@",[NSThread currentThread]);
    dispatch_sync(dispatch_get_main_queue(), ^{
        for (int i = 0; i<2; i++) {
            sleep(1);
            NSLog(@"主隊列同步任務1 = %d",i);
            NSLog(@"當前線程:%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(dispatch_get_main_queue(), ^{
        for (int i = 0; i<2; i++) {
            sleep(1);
            NSLog(@"主隊列同步任務2 = %d",i);
            NSLog(@"當前線程:%@",[NSThread currentThread]);
        }
    });
}
複製代碼

執行結果

主隊列+異步任務

因爲主隊列是串行隊列,因此主隊列+異步任務就至關於串行隊列+異步任務

代碼

-(void)mainAsysncText {
    NSLog(@"當前所在的線程:%@",[NSThread currentThread]);
    NSLog(@"主隊列異步任務開始");
    dispatch_async(dispatch_get_main_queue(), ^{
        for (int i = 0; i<2; i++) {
            sleep(1);
            NSLog(@"主隊列異步任務1 = %d",i);
            NSLog(@"當前線程:%@",[NSThread currentThread]);
        }
    });
    dispatch_async(dispatch_get_main_queue(), ^{
        for (int i = 0; i<2; i++) {
            sleep(1);
            NSLog(@"主隊列異步任務2 = %d",i);
            NSLog(@"當前線程:%@",[NSThread currentThread]);
        }
    });
    NSLog(@"主隊列異步任務結束");
}
複製代碼

運行結果

效果等同於串行隊列+異步任務

相關文章
相關標籤/搜索