參考:https://juejin.im/post/5a90de68f265da4e9b592b40
基礎API
// 系統(tǒng)標準提供的兩個隊列
// 全局隊列,也是一個并行隊列
dispatch_get_global_queue
// 主隊列普办,在主線程中運行液兽,因為主線程只有一個,所以這是一個串行隊列
dispatch_get_main_queue
// 除此之外,還可以自己生成隊列
// 串行隊列(串行隊列每次只有一個任務被執(zhí)行,任務一個接一個按順序執(zhí)行)
dispatch_queue_create("隊列名", DISPATCH_QUEUE_SERIAL)
// 并行隊列(雖然并發(fā)隊列可以開啟多個線程蠢莺,并且同時執(zhí)行多個任務。但是因為本身不能創(chuàng)建新線程)
dispatch_queue_create("隊列名", DISPATCH_QUEUE_CONCURRENT)
// 接下來是同步與異步線程的創(chuàng)建:
dispatch_sync(..., ^(block)) // 同步線程
dispatch_async(..., ^(block)) // 異步線程
//串行隊列+同步線程不會開啟新線程
1.串行隊列:任務按順序執(zhí)行零如,一個執(zhí)行完畢執(zhí)行下一個任務躏将,無論同步任務或是異步任務,因為串行隊列 執(zhí)行同步任務不開辟線程埠况,執(zhí)行異步任務開辟最多開辟一條線程,所以必須順序執(zhí)行棵癣。
2.并行隊列的特點辕翰,執(zhí)行異步任務具備開辟多條線程的能力,執(zhí)行同步任務狈谊,順序執(zhí)行喜命。因為沒有開辟線程沟沙。
3.開不開線程,取決的任務壁榕,同步不開新線程矛紫,異步開新線程。
4.開幾條線程取決于隊列牌里,串行隊列開一條線程颊咬,并行隊列在執(zhí)行多個異步任務時會開辟多條線程。
dispatch_group 隊列組
分別異步執(zhí)行2個耗時任務牡辽,然后當2個耗時任務都執(zhí)行完畢后再回到主線程執(zhí)行任務喳篇。這時候我們可以用到 GCD 的隊列組。
//第一步創(chuàng)建隊列
dispatch_queue_t customQuue = dispatch_queue_create("隊列名稱", DISPATCH_QUEUE_CONCURRENT);
//第二步創(chuàng)建組
dispatch_group_t customGroup = dispatch_group_create();
//第三步添加任務
dispatch_group_async(customGroup, customQuue, ^{
NSLog(@"吃飯");
});
dispatch_group_async(customGroup, customQuue, ^{
NSLog(@"洗澡");
});
//第四步通知
dispatch_group_notify(customGroup, dispatch_get_main_queue(), ^{
NSLog(@"任務完成");
});
//dispatch_group_wait暫停當前線程(**阻塞當前線程**)态辛,等待指定的 group 中的任務執(zhí)行完成后麸澜,才會往下繼續(xù)執(zhí)行。
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//這里的dispatch_group_enter奏黑、dispatch_group_leave組合炊邦,其實等同于dispatch_group_async。
dispatch_group_enter(customGroup);
dispatch_async(customQuue, ^{
// 追加任務1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當前線程
}
dispatch_group_leave(group);
});
dispatch_semaphore 信號量
GCD 中的信號量是指 Dispatch Semaphore熟史,是持有計數(shù)的信號馁害。類似于過高速路收費站的欄桿∫怨剩可以通過時蜗细,打開欄桿,不可以通過時怒详,關閉欄桿炉媒。在 Dispatch Semaphore 中,使用計數(shù)來完成這個功能昆烁,計數(shù)為0時等待吊骤,不可通過。計數(shù)為1或大于1時静尼,計數(shù)減1且不等待白粉,可通過。
Dispatch Semaphore 提供了三個函數(shù)鼠渺。
1.dispatch_semaphore_create:創(chuàng)建一個Semaphore并初始化信號的總量
2.dispatch_semaphore_signal:發(fā)送一個信號鸭巴,讓信號總量加1
3.dispatch_semaphore_wait:可以使總信號量減1,當信號總量為0時就會一直等待(阻塞所在線程)拦盹,否則就可以正常執(zhí)行鹃祖。
注意:信號量的使用前提是:想清楚你需要處理哪個線程等待(阻塞),又要哪個線程繼續(xù)執(zhí)行普舆,然后使用信號量恬口。
Dispatch Semaphore 在實際開發(fā)中主要用于:
保持線程同步校读,將異步執(zhí)行任務轉(zhuǎn)換為同步執(zhí)行任務
保證線程安全,為線程加鎖
上代碼
/**
* 線程安全:使用 semaphore 加鎖
* 初始化火車票數(shù)量祖能、賣票窗口(線程安全)歉秫、并開始賣票
*/
- (void)initTicketStatusSave {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程
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 代表上海火車票售賣窗口
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) {
// 相當于加鎖
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 { //如果已賣完雁芙,關閉售票窗口
NSLog(@"所有火車票均已售完");
// 相當于解鎖
dispatch_semaphore_signal(semaphoreLock);
break;
}
// 相當于解鎖
dispatch_semaphore_signal(semaphoreLock);
}
}
輸出結果為:
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ù),在進程管理中起到一個柵欄的作用揭厚,它等待所有位于barrier函數(shù)之前的操作執(zhí)行完畢后執(zhí)行,并且在barrier函數(shù)執(zhí)行之后,barrier函數(shù)之后的操作才會得到執(zhí)行却特,該函數(shù)需要同dispatch_queue_create函數(shù)生成的DISPATCH_QUEUE_CONCURRENT隊列一起使用。
柵欄函數(shù)的這個特點筛圆,使得它非常適合用于做多讀單寫讀寫鎖裂明。比如說對于一個數(shù)據(jù),可以多線程讀取太援,但是只能單線程修改闽晦,就非常適合用柵欄函數(shù)dispatch_barrier和同步函數(shù)dispatch_sync配合并行隊列做數(shù)據(jù)的讀寫安全機制。
柵欄函數(shù)使用注意事項
為什么不能跟全局并行隊列配合使用呢提岔?
原因在于全局隊列屬于系統(tǒng)創(chuàng)建并管理仙蛉,這個隊列不止我們app在用,系統(tǒng)也在用碱蒙。里面很多涉及到系統(tǒng)自身相關的操作荠瘪,一旦我們外部app阻塞這個隊列,有可能會影響系統(tǒng)相關的操作赛惩。因此柵欄函數(shù)對全局(globa)并行隊列的操作是無效的 (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待更新