iOS多線程之GCD

相關文章:
iOS多線程之NSThread
iOS多線程之NSOperations

隊列

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

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

提交任務(block)到隊列(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í)行時,隊列中其它block不會被執(zhí)行,即barrier相當于一個狹窄的通道
場景:多線程讀寫競態(tài)資源,多個讀線程間可以并行,但讀寫、寫寫線程間只能串行,這時可以:
1)使用并發(fā)隊列(為了防止barrier特性影響其它線程,不要使用dispatch_get_global_queue,而是使用dispatch_queue_create來創(chuàng)建新隊列)
2)使用dispatch_barrier_async添加寫block,保證隊列中寫block執(zhí)行時不會有其它讀寫block正在執(zhí)行
barrier執(zhí)行模型

注意:
如果你調(diào)用 dispatch_sync 并放在你已運行著的當前串行隊列。這會導致死鎖,因為調(diào)用會一直等待直到 Block 完成,但 Block 不能完成(它甚至不會開始?。?,直到當前已經(jīng)存在的任務完成,而當前任務無法完成!舉個例子:

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

單例

dispatch_once() 以線程安全的方式執(zhí)行且僅執(zhí)行其代碼塊一次。試圖訪問臨界區(qū)(即傳遞給 dispatch_once 的代碼)的不同的線程會在臨界區(qū)已有一個線程的情況下被阻塞,直到臨界區(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 會在整個組的任務都完成時通知你。這些任務可以是同步的,也可以是異步的,即便在不同的隊列也行。而且在整個組的任務都完成時,Dispatch Group 可以用同步的或者異步的方式通知你。

創(chuàng)建group Api 備注
dispatch_group_t dispatch_group_create(void) 創(chuàng)建一個group
將任務(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) 手動表明block進入group,dispatch_group_enterdispatch_group_leave是成對出現(xiàn)的。
dispatch_group_leave(dispatch_group_t group) 手動表明block在group中執(zhí)行完成
設置group完成時的通知/回調(diào) 備注
dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout) 阻塞當前線程,直到group里面所有的任務都完成或者等到某個超時發(fā)生
dispatch_group_notify(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block)
異步設置group里所有任務都完成時,在queue隊列中執(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進入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);
        }];
    }
    // 設置等待3秒鐘
    dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3  * NSEC_PER_SEC));
    if(dispatch_group_wait(group, timeoutTime))
    {
        NSLog(@"圖片下載超時");
    }
    else
    {
        NSLog(@"所有圖片都下載完成");
    }
});

信號量

信號量讓你控制多個消費者對有限數(shù)量資源的訪問
原則:當信號量小于等于0時,dispatch_semaphore_wait會阻塞線程,當dispatch_semaphore_signal時會讓信號量加1,如果這時信號量大于0,則喚醒阻塞的線程。

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

圖片下載示例:

// 創(chuàng)建信號量,并且初始化為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) {
    // 設置信號量+1,此時信號量大于0,會喚醒等待的線程。
    dispatch_semaphore_signal(semaphore);
}];
// 設置等待3秒鐘
dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 \\* NSEC_PER_SEC));
// 此時線程會阻塞,只到圖片下載完成或者等待超時
if(dispatch_semaphore_wait(semaphore, timeoutTime))
{
    NSLog(@"等待超時");
}
else
{
    NSLog(@"圖片下載完畢");
}

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

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • GCD (Grand Central Dispatch) :iOS4 開始引入,使用更加方便,程序員只需要將任務添...
    池鵬程閱讀 1,440評論 0 2
  • 原創(chuàng)文章 轉載請注明出處, 謝謝! (~ o ~)Y 本文思維導圖 GCD是什么 全稱是 Grand Centra...
    Jimmy_P閱讀 4,809評論 10 66
  • 前言 在學習Android編程的時候,我們經(jīng)常會使用 runOnUiThread(),把UI相關的操作放到里面。而...
    ChenJZ閱讀 11,897評論 22 36
  • 多線程 在iOS開發(fā)中為提高程序的運行效率會將比較耗時的操作放在子線程中執(zhí)行,iOS系統(tǒng)進程默認啟動一個主線程,用...
    郭豪豪閱讀 2,720評論 0 4
  • 本篇文章是iOS多線程系列的第二篇文章,之所以將GCD放在第二篇介紹,是因為理解了GCD后就比較容易理解NSOpe...
    Neebel閱讀 818評論 2 10

友情鏈接更多精彩內(nèi)容