隊列的幾種創(chuàng)建方式
-
串行隊列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
serialQueue:表示隊列的名稱
DISPATCH_QUEUE_SERIAL:表示為串行隊列
dispatch_queue_t nullQueue = dispatch_queue_create("nullQueue", NULL);
后面標(biāo)注的NULL也是串行隊列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
主隊列是一種特殊的串行隊列
-
并發(fā)隊列
dispatch_queue_t concurrentQueue = dispatch_queue_create("asynQueue", DISPATCH_QUEUE_CONCURRENT);
DISPATCH_QUEUE_CONCURRENT:表示為并發(fā)隊列
dispatch_queue_t highGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
并發(fā)隊列+優(yōu)先級高
dispatch_queue_t defaultGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"defaultGlobalQueue-----%@",defaultGlobalQueue);
并發(fā)隊列+優(yōu)先級默認(rèn)
dispatch_queue_t lowGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
NSLog(@"lowGlobalQueue-----%@",lowGlobalQueue)
并發(fā)隊列+優(yōu)先級低
dispatch_queue_t backGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
NSLog(@"backGlobalQueue-----%@",backGlobalQueue);
并發(fā)隊列+優(yōu)先級最低
優(yōu)先級高的隊列里的任務(wù)會先執(zhí)行恢暖,之后才執(zhí)行優(yōu)先級低的隊列中的任務(wù)(需要注意:十次里面可能有九次都是等待優(yōu)先級高的隊列里的任務(wù)全部執(zhí)行完畢才開始執(zhí)行優(yōu)先級低的隊列中的任務(wù)下隧,但是也會出現(xiàn)優(yōu)先級高的隊列中的任務(wù)沒有完全執(zhí)行完成就開始執(zhí)行優(yōu)先級低的隊列中的任務(wù))
親測如下(測試很多次才可能出現(xiàn)一次):highGlobalQueue中i=9的那次打印還沒執(zhí)行,lowGlobalQueue中i=0的那次打印就執(zhí)行了
for (int i = 0; i< 10;i++){
dispatch_async(lowGlobalQueue, ^{
NSLog(@"backGlobalQueue--%@--%d",[NSThread currentThread], i);
});
dispatch_async(highGlobalQueue, ^{
NSLog(@"lowGlobalQueue--%@--%d",[NSThread currentThread], i);
});
}
打印結(jié)果如下
2018-08-22 16:35:50.621567+0800 GCD_Demo[92525:3732297] highGlobalQueue--<NSThread: 0x604000473900>{number = 183, name = (null)}--0
2018-08-22 16:35:50.621647+0800 GCD_Demo[92525:3732204] highGlobalQueue--<NSThread: 0x6040004737c0>{number = 174, name = (null)}--1
2018-08-22 16:35:50.621685+0800 GCD_Demo[92525:3732299] highGlobalQueue--<NSThread: 0x604000472280>{number = 185, name = (null)}--2
2018-08-22 16:35:50.621698+0800 GCD_Demo[92525:3732198] highGlobalQueue--<NSThread: 0x60000027cb80>{number = 169, name = (null)}--3
2018-08-22 16:35:50.621722+0800 GCD_Demo[92525:3732207] highGlobalQueue--<NSThread: 0x604000472a40>{number = 177, name = (null)}--4
2018-08-22 16:35:50.621728+0800 GCD_Demo[92525:3732201] highGlobalQueue--<NSThread: 0x600000275f00>{number = 171, name = (null)}--5
2018-08-22 16:35:50.621738+0800 GCD_Demo[92525:3732203] highGlobalQueue--<NSThread: 0x6040004741c0>{number = 173, name = (null)}--6
2018-08-22 16:35:50.621797+0800 GCD_Demo[92525:3732295] highGlobalQueue--<NSThread: 0x60400046df40>{number = 181, name = (null)}--7
2018-08-22 16:35:50.621817+0800 GCD_Demo[92525:3732296] highGlobalQueue--<NSThread: 0x60400046a100>{number = 182, name = (null)}--9
2018-08-22 16:35:50.621761+0800 GCD_Demo[92525:3732206] lowGlobalQueue--<NSThread: 0x604000470880>{number = 176, name = (null)}--0
2018-08-22 16:35:50.621823+0800 GCD_Demo[92525:3731738] highGlobalQueue--<NSThread: 0x604000473040>{number = 154, name = (null)}--8
2018-08-22 16:35:50.622011+0800 GCD_Demo[92525:3732298] lowGlobalQueue--<NSThread: 0x60400046a300>{number = 184, name = (null)}--1
2018-08-22 16:35:50.622329+0800 GCD_Demo[92525:3732265] lowGlobalQueue--<NSThread: 0x604000473c80>{number = 179, name = (null)}--3
2018-08-22 16:35:50.622349+0800 GCD_Demo[92525:3732294] lowGlobalQueue--<NSThread: 0x604000472c80>{number = 180, name = (null)}--2
2018-08-22 16:35:50.622505+0800 GCD_Demo[92525:3732299] lowGlobalQueue--<NSThread: 0x604000472280>{number = 185, name = (null)}--5
2018-08-22 16:35:50.622406+0800 GCD_Demo[92525:3732204] lowGlobalQueue--<NSThread: 0x6040004737c0>{number = 174, name = (null)}--4
2018-08-22 16:35:50.622536+0800 GCD_Demo[92525:3732198] lowGlobalQueue--<NSThread: 0x60000027cb80>{number = 169, name = (null)}--6
2018-08-22 16:35:50.623330+0800 GCD_Demo[92525:3732207] lowGlobalQueue--<NSThread: 0x604000472a40>{number = 177, name = (null)}--7
2018-08-22 16:35:50.623420+0800 GCD_Demo[92525:3732297] lowGlobalQueue--<NSThread: 0x604000473900>{number = 183, name = (null)}--8
2018-08-22 16:35:50.623996+0800 GCD_Demo[92525:3732295] lowGlobalQueue--<NSThread: 0x60400046df40>{number = 181, name = (null)}--9
GCD的常見用法
-
dispatch_apply
該函數(shù)指定次數(shù)將指定的Block追加到指定的Queue中厢洞,并等待全部處理執(zhí)行結(jié)束
第一個參數(shù)是執(zhí)行的次數(shù)
第二個參數(shù)是任務(wù)加入的隊列
第三個block是執(zhí)行的任務(wù)劝堪,需傳入一個次數(shù)的參數(shù)
dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
NSLog(@"dispatch_apply--%@--%ld",[NSThread currentThread], index);
});
-
dispatch_barrier
GCD柵欄函數(shù):在并發(fā)隊列中將此代碼插入的地方上下隔開,如果柵欄一樣呀闻,兩部分不影響精肃。只有上邊的并發(fā)隊列都執(zhí)行結(jié)束之后秤涩,下邊的并發(fā)隊列才能夠執(zhí)行。
如下例子中:指定在任務(wù)1司抱、2(無序)執(zhí)行之后筐眷,執(zhí)行任務(wù)barrier,然后執(zhí)行任務(wù)3习柠、4(無序)
特點(diǎn):指定在任務(wù)1匀谣、2、3资溃、4是在同一個隊列中執(zhí)行才有效
dispatch_barrier_async與dispatch_barrier_sync區(qū)別:
dispatch_barrier_sync代碼后邊的任務(wù)直到dispatch_barrier_sync執(zhí)行完才能被追加到隊列中武翎;
dispatch_barrier_async不用代碼執(zhí)行完,后邊的任務(wù)也會被追加到隊列中肉拓。
代碼上的體現(xiàn)就是dispatch_barrier_sync后邊的代碼不會執(zhí)行后频,dispatch_barrier_async后邊的代碼會執(zhí)行,但是Block不會被執(zhí)行暖途。
dispatch_queue_t queue = dispatch_queue_create("testDispatch_barrier_async", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1-----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"barrier-----%@",[NSThread currentThread]);
});
/*
dispatch_barrier_sync(queue, ^{
NSLog(@"barrier-----%@",[NSThread currentThread]);
});
*/
dispatch_async(queue, ^{
NSLog(@"3-----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"4-----%@",[NSThread currentThread]);
});
NSLog(@"the end");
-
dispatch_group
隊列組:可以監(jiān)聽不同隊列中的多個任務(wù)執(zhí)行結(jié)束之后卑惜,進(jìn)行相應(yīng)的操作,以下兩種法驻售,效果是一樣的
- 隊列組用法示例露久,在指定任務(wù)1、2欺栗、3(無序)都執(zhí)行完成之后毫痕,執(zhí)行相應(yīng)的操作
特點(diǎn):可以是監(jiān)聽多個隊列下的任務(wù)都執(zhí)行完畢
- (void)testDispatch_group {
//創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();
//創(chuàng)建并發(fā)隊列
dispatch_queue_t firstQueue = dispatch_queue_create("firstQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t secondQueue = dispatch_queue_create("secondQueue", DISPATCH_QUEUE_CONCURRENT);
//封裝任務(wù),添加到隊列并監(jiān)聽任務(wù)的執(zhí)行情況
dispatch_group_async(group, firstQueue, ^{
NSLog(@"1-----%@",[NSThread currentThread]);
});
dispatch_group_async(group, firstQueue, ^{
NSLog(@"2-----%@",[NSThread currentThread]);
});
dispatch_group_async(group, secondQueue, ^{
NSLog(@"3-----%@",[NSThread currentThread]);
});
dispatch_group_async(group, secondQueue, ^{
NSLog(@"4-----%@",[NSThread currentThread]);
});
dispatch_group_async_f(group, secondQueue, @"testText", functionWotk);
/*
攔截通知迟几,監(jiān)聽到所有所有任務(wù)執(zhí)行完畢消请,執(zhí)行相應(yīng)的操作
下面的group指的是在哪個隊列組,而firstQueue指的是block任務(wù)在哪個對列中執(zhí)行
dispatch_group_notify 內(nèi)部是異步執(zhí)行
*/
dispatch_group_notify(group, firstQueue, ^{
NSLog(@"the notify");
});
NSLog(@"the end");
}
void functionWotk (void * text) {
NSLog(@"dispatch_group_async_f-----%@-----%@",[NSThread currentThread],text);
}
- 隊列組用法示例
- (void)testDispatch_groupEnterAndLeave {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("testDispatch_groupEnterAndLeave", DISPATCH_QUEUE_CONCURRENT);
/*
在該方法后面的任務(wù)會被隊列組監(jiān)聽
dispatch_group_enter與dispatch_group_leave成對使用类腮,對應(yīng)一進(jìn)一出
*/
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"1-----%@",[NSThread currentThread]);
//監(jiān)聽到該任務(wù)執(zhí)行完畢
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"2-----%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"3-----%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"the notify");
});
}
-
dispatch_semaphore
根據(jù)信號量實現(xiàn)多線程同步機(jī)制臊泰,用來管理對資源的并發(fā)訪問
dispatch_semaphore
有以下三個函數(shù)
dispatch_semaphore_t dispatch_semaphore_create(1);
// 創(chuàng)建信號量,參數(shù):信號量的初值蚜枢,如果不大于0則會返回NULL
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
// 等待降低信號量缸逃,接收一個信號和時間值(多為DISPATCH_TIME_FOREVER);若信號的信號量為0厂抽,則會阻塞當(dāng)前線程需频,直到信號量大于0或者經(jīng)過輸入的時間值;若信號量大于0筷凤,則會使信號量減1并返回昭殉,程序繼續(xù)住下執(zhí)行
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);
// 提高信號量, 使信號量加1并返回
下面提供的是一個出售火車票的場景用例
/**
* 線程安全:使用 semaphore 加鎖
* 初始化火車票數(shù)量、賣票窗口(線程安全)饲化、并開始賣票
*/
- (void)testTicketSale {
NSLog(@"semaphore---begin");
__block NSInteger ticketSurplusCount = 50;
dispatch_semaphore_t semaphoreLock = dispatch_semaphore_create(1);
// queue1 代表火車票售賣窗口1
dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
// queue2 代表火車票售賣窗2
dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue1, ^{
while (1) {
dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);// 相當(dāng)于加鎖
if (ticketSurplusCount > 0) {//如果還有票莽鸭,繼續(xù)售賣
ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票數(shù):%ld 窗口:queue1",ticketSurplusCount]);
[NSThread sleepForTimeInterval:0.2];
dispatch_semaphore_signal(semaphoreLock);// 相當(dāng)于解鎖
} else { //如果已賣完,關(guān)閉售票窗口
NSLog(@"所有火車票均已售完");
dispatch_semaphore_signal(semaphoreLock);// 相當(dāng)于解鎖
break;
}
}
});
dispatch_async(queue2, ^{
while (1) {
dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);// 相當(dāng)于加鎖
if (ticketSurplusCount > 0) {//如果還有票吃靠,繼續(xù)售賣
ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票數(shù):%ld 窗口:queue2",ticketSurplusCount]);
[NSThread sleepForTimeInterval:0.2];
dispatch_semaphore_signal(semaphoreLock);// 相當(dāng)于解鎖
} else { //如果已賣完硫眨,關(guān)閉售票窗口
NSLog(@"所有火車票均已售完");
dispatch_semaphore_signal(semaphoreLock);// 相當(dāng)于解鎖
break;
}
}
});
//當(dāng)線程1執(zhí)行到dispatch_semaphore_wait這一行時,semaphore的信號量為1巢块,所以使信號量-1變?yōu)?礁阁,并且線程1繼續(xù)往下執(zhí)行;如果當(dāng)在線程1售票操作還沒執(zhí)行完的時候族奢,又有線程2來訪問姥闭,執(zhí)行dispatch_semaphore_wait時由于此時信號量為0,且時間為DISPATCH_TIME_FOREVER,所以會一直阻塞線程2(此時線程2處于等待狀態(tài))越走,直到線程1執(zhí)行完售票操作并且執(zhí)行完dispatch_semaphore_signal使信號量為1后棚品,線程2才能解除阻塞繼續(xù)住下執(zhí)行。以上可以保證同時只有一個線程執(zhí)行售票操作這部分block中的代碼
}
如果有地方理解錯誤廊敌,歡迎各位大佬指正铜跑,十分感謝!
推薦幾篇本文參考的博文
iOS超級超級詳細(xì)介紹GCD:對GCD的用法原理講的通俗骡澈,容易理解
iOS多線程:『GCD』詳盡總結(jié):十分詳盡
iOS多線程之GCD:對GCD API的標(biāo)注很全面