參考:https://juejin.im/post/5a90de68f265da4e9b592b40
基礎(chǔ)API
// 系統(tǒng)標(biāo)準(zhǔn)提供的兩個(gè)隊(duì)列
// 全局隊(duì)列,也是一個(gè)并行隊(duì)列
dispatch_get_global_queue
// 主隊(duì)列,在主線程中運(yùn)行,因?yàn)橹骶€程只有一個(gè),所以這是一個(gè)串行隊(duì)列
dispatch_get_main_queue
// 除此之外,還可以自己生成隊(duì)列
// 串行隊(duì)列(串行隊(duì)列每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)一個(gè)接一個(gè)按順序執(zhí)行)
dispatch_queue_create("隊(duì)列名", DISPATCH_QUEUE_SERIAL)
// 并行隊(duì)列(雖然并發(fā)隊(duì)列可以開啟多個(gè)線程,并且同時(shí)執(zhí)行多個(gè)任務(wù)。但是因?yàn)楸旧聿荒軇?chuàng)建新線程)
dispatch_queue_create("隊(duì)列名", DISPATCH_QUEUE_CONCURRENT)
// 接下來(lái)是同步與異步線程的創(chuàng)建:
dispatch_sync(..., ^(block)) // 同步線程
dispatch_async(..., ^(block)) // 異步線程
//串行隊(duì)列+同步線程不會(huì)開啟新線程
1.串行隊(duì)列:任務(wù)按順序執(zhí)行,一個(gè)執(zhí)行完畢執(zhí)行下一個(gè)任務(wù),無(wú)論同步任務(wù)或是異步任務(wù),因?yàn)榇嘘?duì)列 執(zhí)行同步任務(wù)不開辟線程,執(zhí)行異步任務(wù)開辟最多開辟一條線程,所以必須順序執(zhí)行。
2.并行隊(duì)列的特點(diǎn),執(zhí)行異步任務(wù)具備開辟多條線程的能力,執(zhí)行同步任務(wù),順序執(zhí)行。因?yàn)闆]有開辟線程。
3.開不開線程,取決的任務(wù),同步不開新線程,異步開新線程。
4.開幾條線程取決于隊(duì)列,串行隊(duì)列開一條線程,并行隊(duì)列在執(zhí)行多個(gè)異步任務(wù)時(shí)會(huì)開辟多條線程。
dispatch_group 隊(duì)列組
分別異步執(zhí)行2個(gè)耗時(shí)任務(wù),然后當(dāng)2個(gè)耗時(shí)任務(wù)都執(zhí)行完畢后再回到主線程執(zhí)行任務(wù)。這時(shí)候我們可以用到 GCD 的隊(duì)列組。
//第一步創(chuàng)建隊(duì)列
dispatch_queue_t customQuue = dispatch_queue_create("隊(duì)列名稱", DISPATCH_QUEUE_CONCURRENT);
//第二步創(chuàng)建組
dispatch_group_t customGroup = dispatch_group_create();
//第三步添加任務(wù)
dispatch_group_async(customGroup, customQuue, ^{
NSLog(@"吃飯");
});
dispatch_group_async(customGroup, customQuue, ^{
NSLog(@"洗澡");
});
//第四步通知
dispatch_group_notify(customGroup, dispatch_get_main_queue(), ^{
NSLog(@"任務(wù)完成");
});
//dispatch_group_wait暫停當(dāng)前線程(**阻塞當(dāng)前線程**),等待指定的 group 中的任務(wù)執(zhí)行完成后,才會(huì)往下繼續(xù)執(zhí)行。
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//這里的dispatch_group_enter、dispatch_group_leave組合,其實(shí)等同于dispatch_group_async。
dispatch_group_enter(customGroup);
dispatch_async(customQuue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
dispatch_group_leave(group);
});
dispatch_semaphore 信號(hào)量
GCD 中的信號(hào)量是指 Dispatch Semaphore,是持有計(jì)數(shù)的信號(hào)。類似于過(guò)高速路收費(fèi)站的欄桿。可以通過(guò)時(shí),打開欄桿,不可以通過(guò)時(shí),關(guān)閉欄桿。在 Dispatch Semaphore 中,使用計(jì)數(shù)來(lái)完成這個(gè)功能,計(jì)數(shù)為0時(shí)等待,不可通過(guò)。計(jì)數(shù)為1或大于1時(shí),計(jì)數(shù)減1且不等待,可通過(guò)。
Dispatch Semaphore 提供了三個(gè)函數(shù)。
1.dispatch_semaphore_create:創(chuàng)建一個(gè)Semaphore并初始化信號(hào)的總量
2.dispatch_semaphore_signal:發(fā)送一個(gè)信號(hào),讓信號(hào)總量加1
3.dispatch_semaphore_wait:可以使總信號(hào)量減1,當(dāng)信號(hào)總量為0時(shí)就會(huì)一直等待(阻塞所在線程),否則就可以正常執(zhí)行。
注意:信號(hào)量的使用前提是:想清楚你需要處理哪個(gè)線程等待(阻塞),又要哪個(gè)線程繼續(xù)執(zhí)行,然后使用信號(hào)量。
Dispatch Semaphore 在實(shí)際開發(fā)中主要用于:
保持線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)
保證線程安全,為線程加鎖
上代碼
/**
* 線程安全:使用 semaphore 加鎖
* 初始化火車票數(shù)量、賣票窗口(線程安全)、并開始賣票
*/
- (void)initTicketStatusSave {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"semaphore---begin");
semaphoreLock = dispatch_semaphore_create(1);
self.ticketSurplusCount = 50;
// queue1 代表北京火車票售賣窗口
dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
// queue2 代表上?;疖嚻笔圪u窗口
dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
__weak typeof(self) weakSelf = self;
dispatch_async(queue1, ^{
[weakSelf saleTicketSafe];
});
dispatch_async(queue2, ^{
[weakSelf saleTicketSafe];
});
}
/**
* 售賣火車票(線程安全)
*/
- (void)saleTicketSafe {
while (1) {
// 相當(dāng)于加鎖
dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
if (self.ticketSurplusCount > 0) { //如果還有票,繼續(xù)售賣
self.ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票數(shù):%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
} else { //如果已賣完,關(guān)閉售票窗口
NSLog(@"所有火車票均已售完");
// 相當(dāng)于解鎖
dispatch_semaphore_signal(semaphoreLock);
break;
}
// 相當(dāng)于解鎖
dispatch_semaphore_signal(semaphoreLock);
}
}
輸出結(jié)果為:
2018-02-23 22:32:19.814232+0800 YSC-GCD-demo[20862:5290531] currentThread---<NSThread: 0x6000000783c0>{number = 1, name = main}
2018-02-23 22:32:19.814412+0800 YSC-GCD-demo[20862:5290531] semaphore---begin
2018-02-23 22:32:19.814837+0800 YSC-GCD-demo[20862:5290687] 剩余票數(shù):49 窗口:<NSThread: 0x6040002709c0>{number = 3, name = (null)}
2018-02-23 22:32:20.017745+0800 YSC-GCD-demo[20862:5290689] 剩余票數(shù):48 窗口:<NSThread: 0x60000046c640>{number = 4, name = (null)}
2018-02-23 22:32:20.222039+0800 YSC-GCD-demo[20862:5290687] 剩余票數(shù):47 窗口:<NSThread: 0x6040002709c0>{number = 3, name = (null)}
......
2018-02-23 22:32:29.024817+0800 YSC-GCD-demo[20862:5290689] 剩余票數(shù):4 窗口:<NSThread: 0x60000046c640>{number = 4, name = (null)}
2018-02-23 22:32:29.230110+0800 YSC-GCD-demo[20862:5290687] 剩余票數(shù):3 窗口:<NSThread: 0x6040002709c0>{number = 3, name = (null)}
2018-02-23 22:32:29.433615+0800 YSC-GCD-demo[20862:5290689] 剩余票數(shù):2 窗口:<NSThread: 0x60000046c640>{number = 4, name = (null)}
2018-02-23 22:32:29.637572+0800 YSC-GCD-demo[20862:5290687] 剩余票數(shù):1 窗口:<NSThread: 0x6040002709c0>{number = 3, name = (null)}
2018-02-23 22:32:29.840234+0800 YSC-GCD-demo[20862:5290689] 剩余票數(shù):0 窗口:<NSThread: 0x60000046c640>{number = 4, name = (null)}
2018-02-23 22:32:30.044960+0800 YSC-GCD-demo[20862:5290687] 所有火車票均已售完
2018-02-23 22:32:30.045260+0800 YSC-GCD-demo[20862:5290689] 所有火車票均已售完
柵欄函數(shù)dispatch_barrier_async
dispatch_barrier_async函數(shù),在進(jìn)程管理中起到一個(gè)柵欄的作用,它等待所有位于barrier函數(shù)之前的操作執(zhí)行完畢后執(zhí)行,并且在barrier函數(shù)執(zhí)行之后,barrier函數(shù)之后的操作才會(huì)得到執(zhí)行,該函數(shù)需要同dispatch_queue_create函數(shù)生成的DISPATCH_QUEUE_CONCURRENT隊(duì)列一起使用。
柵欄函數(shù)的這個(gè)特點(diǎn),使得它非常適合用于做多讀單寫讀寫鎖。比如說(shuō)對(duì)于一個(gè)數(shù)據(jù),可以多線程讀取,但是只能單線程修改,就非常適合用柵欄函數(shù)dispatch_barrier和同步函數(shù)dispatch_sync配合并行隊(duì)列做數(shù)據(jù)的讀寫安全機(jī)制。
柵欄函數(shù)使用注意事項(xiàng)
為什么不能跟全局并行隊(duì)列配合使用呢?
原因在于全局隊(duì)列屬于系統(tǒng)創(chuàng)建并管理,這個(gè)隊(duì)列不止我們app在用,系統(tǒng)也在用。里面很多涉及到系統(tǒng)自身相關(guān)的操作,一旦我們外部app阻塞這個(gè)隊(duì)列,有可能會(huì)影響系統(tǒng)相關(guān)的操作。因此柵欄函數(shù)對(duì)全局(globa)并行隊(duì)列的操作是無(wú)效的 (dispatch_get_global_queue)
在
dispatch_queue_t 線程名字 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
中
dispatch_async = dispatch_barrier_async
線程加鎖
加鎖方式一 dispatch_semaphore 自旋鎖
加鎖方式二 @synchronized 互斥鎖 @synchronized(self) { ---}
加鎖方式三 NSLock --- [_lock lock]; [_lock unlock];
swfit待更新