iOS_多線程一:GCD

一、基礎概念

1、GCD簡介

Grand Central Dispatch 簡稱(GCD)是蘋果公司開發的技術。以優化應用程序支持多核心處理器和其他的對稱多處理系統的系統。

 •GCD屬於函數級的多線程,性能更高,功能也更加強大。

 •它首次發佈在Mac OS X 10.6 ,iOS 4及以上也可用。

2、GCD核心概念

任務:具有一定功能的代碼段。一般是一個block或者函數。

 •分發隊列:GCD以隊列的方式進行工作,例如FIFO。

 •GCD會根據分發隊列的類型,創建合適數量的線程執行隊列中的任務。

3、GCD中兩種隊列

dispatch queue分爲下面2種:

 •SerialQueue併發隊列:一次只執行一個任務。Serial queue通常用於同步訪問特定的資源或數據。當你創建多個Serial queue時,雖然它們各自是同步執行的,但Serial queue與Serial queue之間是併發執行的。SerialQueue能實現線程同步

 •Concurrent串行隊列:可以併發地執行多個任務,但是遵守FIFO

4、GCD的功能:

dispatch_async()    往隊列中添加任務,任務會排隊執行

dispatch_after()      往隊列中添加任務,任務不但會排隊,還會在延遲的時間點執行

dispatch_apply()    往隊列中添加任務,任務會重複執行n次

dispatch_group_async()   將任務添加到隊列中,並添加分組標記

dispatch_group_notify()    將任務添加到隊列中,當某個分組的所有任務執行完之後,此任務纔會執行

dispatch_barrier_async()  將任務添加到隊列中,此任務執行的時候,其他任務停止執行

dispatch_once()   任務添加到隊列中,但任務在程序運行過程中,只執行一次

dispatch_sync()   將任務添加到隊列中,block不執行完,下面代碼不會執行

dispatch_async_f()  將任務添加到隊列中,任務是函數非block

一些使用案例:

1、延遲執行:

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC));
dispatch_after(time , dispatch_get_main_queue(), ^{
  NSLog(@"延遲5s執行");
});
NSLog(@"是否阻塞主線程"); // 不會

2、只執行一次

static dispatch_once_t oncetoken;
dispatch_once(&oncetoken, ^{
  NSLog(@"只執行一次");
});

可以用來實現單例,很多面試會考,最好能手寫出來:

+ (Class *)shareInstance {
  static Class *instance = nil;
  static dispatch_once_t oncetoken;
  dispatch_once(&oncetoken, ^{
    instance = [[Class alloc] init];
  });
  return instance;
}

3、在子線程中,返回主線程

dispatch_async(dispatch_get_main_queue(), ^{
  NSLog(@"回到主線程");
});

 

一、獲取併發隊列:

方法1:直接使用默認提供的`全局併發隊列`

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // (擴展參數:暫時用不上)
// DISPATCH_QUEUE_PRIORITY_HIGH 2                高
// DISPATCH_QUEUE_PRIORITY_DEFAULT 0             默認
// DISPATCH_QUEUE_PRIORITY_LOW (-2)              低
// DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN  後臺

方法2:自己創建

dispatch_queue_t queue = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_CONCURRENT);

二、獲取串行隊列:

方法1:直接使用主線程

dispatch_queue_t mainQueue = dispatch_get_main_queue();

方法2:自己創建

dispatch_queue_t queue1 = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_SERIAL);

下面來測試混合搭配使用:

1、異步-全局併發:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
  NSLog(@"執行1:%@", [NSThread currentThread]);
  sleep(2);
  NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
  NSLog(@"執行2:%@", [NSThread currentThread]);
  sleep(3);
  NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
  NSLog(@"執行3:%@", [NSThread currentThread]);
  sleep(1);
  NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 不會
// 執行結果:
// 是否阻塞主線程
// 執行1:<NSThread: 0x600000a69280>{number = 5, name = (null)}
// 執行3:<NSThread: 0x600000a54e80>{number = 6, name = (null)}
// 執行2:<NSThread: 0x600000a58780>{number = 7, name = (null)}
// 完成3:<NSThread: 0x600000a54e80>{number = 6, name = (null)}
// 完成1:<NSThread: 0x600000a69280>{number = 5, name = (null)}
// 完成2:<NSThread: 0x600000a58780>{number = 7, name = (null)}

結論1  異步-全局併發:開啓多個線程,併發執行,不阻塞

2、異步-併發

dispatch_queue_t queue = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
  NSLog(@"執行1:%@", [NSThread currentThread]);
  sleep(2);
  NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
  NSLog(@"執行2:%@", [NSThread currentThread]);
  sleep(3);
  NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
  NSLog(@"執行3:%@", [NSThread currentThread]);
  sleep(1);
  NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 不會
// 執行結果:
// 執行1:<NSThread: 0x600002116400>{number = 5, name = (null)}
// 執行2:<NSThread: 0x60000211df80>{number = 6, name = (null)}
// 執行3:<NSThread: 0x600002116440>{number = 7, name = (null)}
// 是否阻塞主線程
// 完成3:<NSThread: 0x600002116440>{number = 7, name = (null)}
// 完成1:<NSThread: 0x600002116400>{number = 5, name = (null)}
// 完成2:<NSThread: 0x60000211df80>{number = 6, name = (null)}

結論2  異步-併發:開啓多個線程,併發執行,不阻塞

3、異步-主串行

dispatch_async(dispatch_get_main_queue(), ^{
  NSLog(@"執行1:%@", [NSThread currentThread]);
  sleep(2);
  NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_async(dispatch_get_main_queue(), ^{
  NSLog(@"執行2:%@", [NSThread currentThread]);
  sleep(3);
  NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_async(dispatch_get_main_queue(), ^{
  NSLog(@"執行3:%@", [NSThread currentThread]);
  sleep(1);
  NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 不會
// 執行結果:
// 是否阻塞主線程
// 執行1:<NSThread: 0x60000088cdc0>{number = 1, name = main}
// 完成1:<NSThread: 0x60000088cdc0>{number = 1, name = main}
// 執行2:<NSThread: 0x60000088cdc0>{number = 1, name = main}
// 完成2:<NSThread: 0x60000088cdc0>{number = 1, name = main}
// 執行3:<NSThread: 0x60000088cdc0>{number = 1, name = main}
// 完成3:<NSThread: 0x60000088cdc0>{number = 1, name = main}

結論3  異步-主串行:主線程中,順序執行,不阻塞

4、異步-串行

dispatch_queue_t queue = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
  NSLog(@"執行1:%@", [NSThread currentThread]);
  sleep(2);
  NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
  NSLog(@"執行2:%@", [NSThread currentThread]);
  sleep(3);
  NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
  NSLog(@"執行3:%@", [NSThread currentThread]);
  sleep(1);
  NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 不會
// 執行結果:
// 是否阻塞主線程
// 執行1:<NSThread: 0x600003e45200>{number = 3, name = (null)}
// 完成1:<NSThread: 0x600003e45200>{number = 3, name = (null)}
// 執行2:<NSThread: 0x600003e45200>{number = 3, name = (null)}
// 完成2:<NSThread: 0x600003e45200>{number = 3, name = (null)}
// 執行3:<NSThread: 0x600003e45200>{number = 3, name = (null)}
// 完成3:<NSThread: 0x600003e45200>{number = 3, name = (null)}

結論4  異步-串行:開啓一個線程,順序執行,不阻塞

5、同步-全局併發

// (如果用async加入,不會跟sync的在一個線程裏)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(queue, ^{
  NSLog(@"執行1:%@", [NSThread currentThread]);
  sleep(2);
  NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
  NSLog(@"執行2:%@", [NSThread currentThread]);
  sleep(3);
  NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
  NSLog(@"執行3:%@", [NSThread currentThread]);
  sleep(1);
  NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 不會
// 執行結果:
// 執行1:<NSThread: 0x600000675880>{number = 1, name = main}
// 完成1:<NSThread: 0x600000675880>{number = 1, name = main}
// 執行2:<NSThread: 0x600000675880>{number = 1, name = main}
// 完成2:<NSThread: 0x600000675880>{number = 1, name = main}
// 執行3:<NSThread: 0x600000675880>{number = 1, name = main}
// 完成3:<NSThread: 0x600000675880>{number = 1, name = main}
// 是否阻塞主線程

結論5  同步-全局併發:在主線程中,順序執行,阻塞

6、同步-併發

dispatch_queue_t queue = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
  NSLog(@"執行1:%@", [NSThread currentThread]);
  sleep(2);
  NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
  NSLog(@"執行2:%@", [NSThread currentThread]);
  sleep(3);
  NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
  NSLog(@"執行3:%@", [NSThread currentThread]);
  sleep(1);
  NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 會 (因爲:沒有開啓新線程)
// 執行結果:
// 執行1:<NSThread: 0x600000ad9580>{number = 1, name = main}
// 完成1:<NSThread: 0x600000ad9580>{number = 1, name = main}
// 執行2:<NSThread: 0x600000ad9580>{number = 1, name = main}
// 完成2:<NSThread: 0x600000ad9580>{number = 1, name = main}
// 執行3:<NSThread: 0x600000ad9580>{number = 1, name = main}
// 完成3:<NSThread: 0x600000ad9580>{number = 1, name = main}
// 是否阻塞主線程

結論6  同步-併發:在主線程中,順序執行,阻塞

7、同步-主串行

// 例:之前在百度面試遇到的題
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{    // 串行 同步隊列
    NSLog(@"2");
});
NSLog(@"3");
// 輸出:1
// 3加入隊列 2加入隊列;FIFO:3等待2執行 而2在3的後面
// 所以造成死鎖(crash: Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0))

結論7  同步-主串行:死鎖,阻塞

8、同步-串行

dispatch_queue_t queue = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
  NSLog(@"執行1:%@", [NSThread currentThread]);
  sleep(2);
  NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
  NSLog(@"執行2:%@", [NSThread currentThread]);
  sleep(3);
  NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
  NSLog(@"執行3:%@", [NSThread currentThread]);
  sleep(1);
  NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 會
// 執行結果:
// 執行1:<NSThread: 0x600003371880>{number = 1, name = main}
// 完成1:<NSThread: 0x600003371880>{number = 1, name = main}
// 執行2:<NSThread: 0x600003371880>{number = 1, name = main}
// 完成2:<NSThread: 0x600003371880>{number = 1, name = main}
// 執行3:<NSThread: 0x600003371880>{number = 1, name = main}
// 完成3:<NSThread: 0x600003371880>{number = 1, name = main}
// 是否阻塞主線程

結論8  同步-串行:主線程中,順序執行,阻塞

總結:

同步:同步函數不具備開啓線程的能力,無論是什麼隊列都不會開啓線程;

異步:異步函數具備開啓線程的能力,開啓幾條線程由隊列決定(串行隊列只會開啓一條新的線程,併發隊列會開啓多條線程)

異步/同步、併發/串行 互相搭配
  異步 async 同步 sync
全局併發 global_queue 開啓多個線程,併發執行,不阻塞 主線程中,順序執行,阻塞
自創併發 SERIAL 開啓多個線程,併發執行,不阻塞 主線程中,順序執行,阻塞
主串行 main_queue 主線程中,順序執行,不阻塞 死鎖 !!!,阻塞
自創串行 CONCURRENT 開啓一個線程,順序執行,不阻塞 主線程中,順序執行,阻塞

 

 

 

 

 

 

同步 和 異步 添加隻影響是不是阻塞當前線程,和任務的串行或並行執行沒有關係

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章