iOS多線程之GCD

相關(guān)文章:
iOS多線程之NSThread
iOS多線程之NSOperations

隊(duì)列

隊(duì)列 Api 備注
主隊(duì)列(main queue) dispatch_get_main_queue() 串行隊(duì)列,可以操縱UI
全局調(diào)度隊(duì)列(Global Dispatch Queues) dispatch_get_global_queue() 并行隊(duì)列,按照?qǐng)?zhí)行優(yōu)先級(jí),分成4種global queue:
DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_PRIORITY_DEFAULT, DISPATCH_QUEUE_PRIORITY_LOW, DISPATCH_QUEUE_PRIORITY_BACKGROUND
自創(chuàng)建隊(duì)列 dispatch_queue_create() 可以創(chuàng)建:
串行(DISPATCH_QUEUE_SERIAL)、
并行(DISPATCH_QUEUE_CONCURRENT)隊(duì)列
并行隊(duì)列 串行隊(duì)列
并行隊(duì)列
串行隊(duì)列

隊(duì)列和線程是兩個(gè)不同的概念。一個(gè)隊(duì)列可以有多個(gè)線程。每個(gè)隊(duì)列中的操作會(huì)在所屬的線程中運(yùn)行。舉個(gè)例子你創(chuàng)建一個(gè)并行隊(duì)列,然后添加三個(gè)操作到里面。隊(duì)列會(huì)發(fā)起三個(gè)單獨(dú)的線程,然后讓所有操作在各自的線程中并發(fā)運(yùn)行。

提交任務(wù)(block)到隊(duì)列(queue)

提交方式 備注
dispatch_async(dispatch_queue_t queue, dispatch_block_t block) 異步提交block到queue
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block) 同步地提交工作并在返回前等待它完成
dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block) 異步提交block到queue,并且延遲執(zhí)行block,
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC))表示3秒后
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block) 異步提交block,但是該block被執(zhí)行時(shí),隊(duì)列中其它block不會(huì)被執(zhí)行,即barrier相當(dāng)于一個(gè)狹窄的通道
場(chǎng)景:多線程讀寫競(jìng)態(tài)資源,多個(gè)讀線程間可以并行,但讀寫、寫寫線程間只能串行,這時(shí)可以:
1)使用并發(fā)隊(duì)列(為了防止barrier特性影響其它線程,不要使用dispatch_get_global_queue,而是使用dispatch_queue_create來(lái)創(chuàng)建新隊(duì)列)
2)使用dispatch_barrier_async添加寫block,保證隊(duì)列中寫block執(zhí)行時(shí)不會(huì)有其它讀寫block正在執(zhí)行
barrier執(zhí)行模型

注意:
如果你調(diào)用 dispatch_sync 并放在你已運(yùn)行著的當(dāng)前串行隊(duì)列。這會(huì)導(dǎo)致死鎖,因?yàn)檎{(diào)用會(huì)一直等待直到 Block 完成,但 Block 不能完成(它甚至不會(huì)開(kāi)始!),直到當(dāng)前已經(jīng)存在的任務(wù)完成,而當(dāng)前任務(wù)無(wú)法完成!舉個(gè)例子:

@implementation ViewController1
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // dispatch_sync同步提交block到main隊(duì)列(當(dāng)前隊(duì)列)并**等待**block執(zhí)行完畢,而由于是串行隊(duì)列,block需要等待當(dāng)前任務(wù)執(zhí)行完畢,雙向等待造成死鎖。
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"hello world");
    });
    // 下面這條NSLog不會(huì)被執(zhí)行,因?yàn)榫€程已經(jīng)死鎖
    NSLog(@"here");
@end
@implementation ViewController2
- (void)viewDidLoad
{
    [super viewDidLoad];
    // 創(chuàng)建一個(gè)并行隊(duì)列,,,,(*注意:*如果將*DISPATCH_QUEUE_CONCURRENT*修改成*DISPATCH_QUEUE_SERIAL*,即創(chuàng)建串行隊(duì)列,就會(huì)發(fā)生死鎖!!!)
    dispatch_queue_t queue = dispatch_queue_create("abc", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        NSLog(@"..1");
        // 因?yàn)槭遣⑿嘘?duì)列,下面的block不需要等待當(dāng)前block執(zhí)行完畢,就不會(huì)發(fā)生死鎖。
        dispatch_sync(queue, ^{
            NSLog(@"..2");
        });
    });
    NSLog(@"here");
@end

單例

dispatch_once() 以線程安全的方式執(zhí)行且僅執(zhí)行其代碼塊一次。試圖訪問(wèn)臨界區(qū)(即傳遞給 dispatch_once 的代碼)的不同的線程會(huì)在臨界區(qū)已有一個(gè)線程的情況下被阻塞,直到臨界區(qū)完成為止。

@implementation User
+ (instancetype)sharedUser
{
    static User *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[User alloc] init];
    });
    return instance;
}
@end

調(diào)度組

Dispatch Group 會(huì)在整個(gè)組的任務(wù)都完成時(shí)通知你。這些任務(wù)可以是同步的,也可以是異步的,即便在不同的隊(duì)列也行。而且在整個(gè)組的任務(wù)都完成時(shí),Dispatch Group 可以用同步的或者異步的方式通知你。

創(chuàng)建group Api 備注
dispatch_group_t dispatch_group_create(void) 創(chuàng)建一個(gè)group
將任務(wù)(block)添加到group 備注
dispatch_group_async(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block)
異步把block提交到queue,并且將block分配到指定的group
dispatch_group_enter(dispatch_group_t group) 手動(dòng)表明block進(jìn)入group,dispatch_group_enterdispatch_group_leave是成對(duì)出現(xiàn)的。
dispatch_group_leave(dispatch_group_t group) 手動(dòng)表明block在group中執(zhí)行完成
設(shè)置group完成時(shí)的通知/回調(diào) 備注
dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout) 阻塞當(dāng)前線程,直到group里面所有的任務(wù)都完成或者等到某個(gè)超時(shí)發(fā)生
dispatch_group_notify(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block)
異步設(shè)置group里所有任務(wù)都完成時(shí),在queue隊(duì)列中執(zhí)行通知回調(diào)block

多圖片下載示例:

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    for(NSInteger i = 0; i < 10; i++)
    {
        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://a/%zd.png", i]];
        // 表示block進(jìn)入group
        dispatch_group_enter(group);
        [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:url options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
            // 表示block執(zhí)行完成
            dispatch_group_leave(group);
        }];
    }
    // 設(shè)置等待3秒鐘
    dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3  * NSEC_PER_SEC));
    if(dispatch_group_wait(group, timeoutTime))
    {
        NSLog(@"圖片下載超時(shí)");
    }
    else
    {
        NSLog(@"所有圖片都下載完成");
    }
});

信號(hào)量

信號(hào)量讓你控制多個(gè)消費(fèi)者對(duì)有限數(shù)量資源的訪問(wèn)
原則:當(dāng)信號(hào)量小于等于0時(shí),dispatch_semaphore_wait會(huì)阻塞線程,當(dāng)dispatch_semaphore_signal時(shí)會(huì)讓信號(hào)量加1,如果這時(shí)信號(hào)量大于0,則喚醒阻塞的線程。

Api 備注
dispatch_semaphore_t dispatch_semaphore_create(long value) 創(chuàng)建一個(gè)信號(hào)量,并且設(shè)置信號(hào)量的初始值
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout) 等待一個(gè)信號(hào)量,如果當(dāng)前信號(hào)量大于0,則信號(hào)量減1,線程往下執(zhí)行。否則線程阻塞,直到被信號(hào)量大于0時(shí)被喚醒或者等待超時(shí)
dispatch_semaphore_signal(dispatch_semaphore_t dsema) 讓信號(hào)量加1,如果當(dāng)前信號(hào)量大于0,則喚醒一個(gè)waiting的線程

圖片下載示例:

// 創(chuàng)建信號(hào)量,并且初始化為0
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// 下載圖片
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:[NSURL URLWithString:@"http://a/b.png"] options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
    // 設(shè)置信號(hào)量+1,此時(shí)信號(hào)量大于0,會(huì)喚醒等待的線程。
    dispatch_semaphore_signal(semaphore);
}];
// 設(shè)置等待3秒鐘
dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 \\* NSEC_PER_SEC));
// 此時(shí)線程會(huì)阻塞,只到圖片下載完成或者等待超時(shí)
if(dispatch_semaphore_wait(semaphore, timeoutTime))
{
    NSLog(@"等待超時(shí)");
}
else
{
    NSLog(@"圖片下載完畢");
}

參考:
GCD 深入理解(一)
GCD 深入理解(二)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • GCD (Grand Central Dispatch) :iOS4 開(kāi)始引入,使用更加方便,程序員只需要將任務(wù)添...
    池鵬程閱讀 1,347評(píng)論 0 2
  • 原創(chuàng)文章 轉(zhuǎn)載請(qǐng)注明出處, 謝謝! (~ o ~)Y 本文思維導(dǎo)圖 GCD是什么 全稱是 Grand Centra...
    Jimmy_P閱讀 4,702評(píng)論 10 67
  • 前言 在學(xué)習(xí)Android編程的時(shí)候,我們經(jīng)常會(huì)使用 runOnUiThread(),把UI相關(guān)的操作放到里面。而...
    ChenJZ閱讀 11,834評(píng)論 22 36
  • 多線程 在iOS開(kāi)發(fā)中為提高程序的運(yùn)行效率會(huì)將比較耗時(shí)的操作放在子線程中執(zhí)行,iOS系統(tǒng)進(jìn)程默認(rèn)啟動(dòng)一個(gè)主線程,用...
    郭豪豪閱讀 2,612評(píng)論 0 4
  • 本篇文章是iOS多線程系列的第二篇文章,之所以將GCD放在第二篇介紹,是因?yàn)槔斫饬薌CD后就比較容易理解NSOpe...
    Neebel閱讀 781評(píng)論 2 10