在最近的 iOS 面試過程中,GCD 是屬于必問的問題州疾,接觸過不少 GCD 相關(guān)的面試題,有關(guān) GCD 的面試點大多數(shù)都是最大線程數(shù)量控制和任務(wù)分組皇拣,在這里做一個總結(jié)严蓖,方便大家進行參考。
GCD 控制線程數(shù)量
GCD 不像 NSOperation 那樣有直接提供線程數(shù)量控制方法,但是通過 GCD 的 semaphore 功能一樣可以達到控制線程數(shù)量的效果谈飒。
- dispatch_semaphore_create(long value); 利用給定的值創(chuàng)建一個新的可計數(shù)的信號量
- dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); 如果信號量大于 0 岂座,信號量減 1 ,執(zhí)行程序杭措。否則等待信號量
- dispatch_semaphore_signal(dispatch_semaphore_t dsema); 增加信號量
// 控制線程數(shù)量
- (void)runMaxThreadCountWithGCD
{
dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentRunMaxThreadCountWithGCD", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t serialQueue = dispatch_queue_create("serialRunMaxThreadCountWithGCD", DISPATCH_QUEUE_SERIAL);
// 創(chuàng)建一個semaphore,并設(shè)置最大信號量费什,最大信號量表示最大線程數(shù)量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
// 使用循環(huán)往串行隊列 serialQueue 增加 10 個任務(wù)
for (int i = 0; i < 10 ; i++) {
dispatch_async(serialQueue, ^{
// 只有當(dāng)信號量大于 0 的時候,線程將信號量減 1手素,程序向下執(zhí)行
// 否則線程會阻塞并且一直等待鸳址,直到信號量大于 0
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(concurrentQueue, ^{
NSLog(@"%@ 執(zhí)行任務(wù)一次 i = %d",[NSThread currentThread],i);
// 當(dāng)線程任務(wù)執(zhí)行完成之后,發(fā)送一個信號泉懦,增加信號量稿黍。
dispatch_semaphore_signal(semaphore);
});
});
}
NSLog(@"%@ 執(zhí)行任務(wù)結(jié)束",[NSThread currentThread]);
}
執(zhí)行結(jié)果如下,只有 number 3 和 number 4 這 2 個線程在執(zhí)行
<NSThread: 0x60c00007c600>{number = 1, name = main} 執(zhí)行任務(wù)結(jié)束
<NSThread: 0x60c00027a340>{number = 3, name = (null)} 執(zhí)行任務(wù)一次 i = 0
<NSThread: 0x608000263a00>{number = 4, name = (null)} 執(zhí)行任務(wù)一次 i = 1
<NSThread: 0x60c00027a340>{number = 3, name = (null)} 執(zhí)行任務(wù)一次 i = 3
<NSThread: 0x608000263a00>{number = 4, name = (null)} 執(zhí)行任務(wù)一次 i = 2
<NSThread: 0x60c00027a340>{number = 3, name = (null)} 執(zhí)行任務(wù)一次 i = 4
<NSThread: 0x608000263a00>{number = 4, name = (null)} 執(zhí)行任務(wù)一次 i = 5
<NSThread: 0x60c00027a340>{number = 3, name = (null)} 執(zhí)行任務(wù)一次 i = 6
<NSThread: 0x608000263a00>{number = 4, name = (null)} 執(zhí)行任務(wù)一次 i = 7
<NSThread: 0x60c00027a340>{number = 3, name = (null)} 執(zhí)行任務(wù)一次 i = 8
<NSThread: 0x608000263a00>{number = 4, name = (null)} 執(zhí)行任務(wù)一次 i = 9
GCD 任務(wù)分組
GCD 的 dispatch_group_t 功能可以將多個任務(wù)分組崩哩,等待分組里面的所有任務(wù)執(zhí)行完成之后巡球,GCD 的 dispatch_group_notify 方法可以通知。通常會配合一些常見的場景來考察邓嘹,比如同時上傳 10 張圖片酣栈,全部上傳完成后通知用戶。
// 任務(wù)分組
- (void)runGroupWithGCD
{
dispatch_queue_t concurrentQueue = dispatch_queue_create("runGroupWithGCD", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
for (int i = 0; i < 10 ; i++) {
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"%@ 執(zhí)行任務(wù)一次",[NSThread currentThread]);
});
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"%@ 執(zhí)行任務(wù)結(jié)束",[NSThread currentThread]);
});
}
將所有的任務(wù)都加入 group 汹押,等待所有的任務(wù)執(zhí)行完成后矿筝,dispatch_group_notify 會被調(diào)用。
<NSThread: 0x608000265180>{number = 4, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x604000079a40>{number = 6, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x60c000268780>{number = 5, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x60c000267dc0>{number = 3, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x608000265080>{number = 9, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x600000265480>{number = 7, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x60c00007f9c0>{number = 8, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x608000264f40>{number = 10, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x604000079a40>{number = 6, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x608000265180>{number = 4, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x60000006d180>{number = 1, name = main} 執(zhí)行任務(wù)結(jié)束
GCD 任務(wù)分組和線程數(shù)量控制
利用 GCD 的 dispatch_group_t 和 semaphore 功能棚贾,我們可以做到控制線程數(shù)量窖维,并且在所有任務(wù)執(zhí)行完成之后得到通知。
// 任務(wù)分組 + 線程數(shù)量控制
- (void)runMaxCountInGroupWithGCD
{
dispatch_queue_t concurrentQueue = dispatch_queue_create("runGroupWithGCD", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
for (int i = 0; i < 10 ; i++) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"%@ 執(zhí)行任務(wù)一次",[NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"%@ 執(zhí)行任務(wù)結(jié)束",[NSThread currentThread]);
});
}
執(zhí)行之后妙痹,我們可以看到既控制了線程數(shù)量铸史,也在執(zhí)行任務(wù)完成之后得到了通知。
<NSThread: 0x604000269b40>{number = 3, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x608000264780>{number = 4, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x604000269b40>{number = 3, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x608000264780>{number = 4, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x608000264780>{number = 4, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x608000264780>{number = 4, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x604000269b40>{number = 3, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x608000264780>{number = 4, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x604000269b40>{number = 3, name = (null)} 執(zhí)行任務(wù)一次
<NSThread: 0x60400007aa40>{number = 1, name = main} 執(zhí)行任務(wù)結(jié)束