Grand Central Dispatch (GCD)

简介:

Grand Central Dispatch (GCD) Reference

Grand Central Dispatch (GCD) comprises language features, runtime libraries, and system enhancements that provide systemic, comprehensive improvements to the support for concurrent code execution on multicore hardware in iOS and OS X.

GCD拥有丰富的语言特性,多样的运行库以及能增强系统效率,对于硬件上多核处理器提供了广泛地系统级别的优化,在多核的iOS OS X硬件体系中,高并发执行能力得到有效地提升.

The BSD subsystem, CoreFoundation, and Cocoa APIs have all been extended to use these enhancements to help both the system and your application to run faster, more efficiently, and with improved responsiveness. Consider how difficult it is for a single application to use multiple cores effectively, let alone doing it on different computers with different numbers of computing cores or in an environment with multiple applications competing for those cores. GCD, operating at the system level, can better accommodate the needs of all running applications, matching them to the available system resources in a balanced fashion.

BSD子系统,CoreFoundation以及Cocoa中的许多APIs都已经大量使用这些新的特性来帮助系统以及你的应用程序提升性能,使其运行得更快,提升交互体验.考虑到要让一款应用能够很有效地利用多核处理器,不管是在不同的电脑上(这个电脑有着不同数量的处理器)或者是同一台电脑上不同应用如何分配多个处理器.GCD,在系统级别上进行运作,能够很好地对所有运行中的程序进行优化,让他们充分利用系统资源的同时,达到一个美妙的平衡.

 

以上是官方文档前两段的翻译,正如描述中所说,GCD偏向系统调用,更加偏向于底层的函数,效率很高,我将我对于GCD使用的理解加以汇总.

线程是最小的执行单位,而线程需要在指定的线程池中才能够执行,以下是创建线程池的方法.

dispatch_queue_t dispatch_queue_create(
   const char *label
   dispatch_queue_attr_t attr);

 

#pragma mark - 创建并行队列线程池,任务并发执行,一起执行
NS_INLINE dispatch_queue_t GCD_create_concurrent_queue(NSString *queueName)
{
    // DISPATCH_QUEUE_CONCURRENT
    // 线程池提供多个线程来执行任务,所以可以按序启动多个任务并发执行
    dispatch_queue_t concurrentQ
    = dispatch_queue_create([queueName cStringUsingEncoding:NSUTF8StringEncoding],
                            DISPATCH_QUEUE_CONCURRENT);
    
    return concurrentQ;
}
#pragma mark - 创建串行队列线程池,任务依次执行,上一个执行完毕才会执行下一个任务
NS_INLINE dispatch_queue_t GCD_create_serial_queue(NSString *queueName)
{
    // DISPATCH_QUEUE_SERIAL
    // 线程池只提供一个线程用来执行任务,所以后一个任务必须等到前一个任务执行结束才能开始(已经测试,一个任务执行完毕后才回去执行另外一个任务)
    dispatch_queue_t serialQ
    = dispatch_queue_create([queueName cStringUsingEncoding:NSUTF8StringEncoding],
                            DISPATCH_QUEUE_SERIAL);
    
    return serialQ;
}

#pragma mark - 销毁线程池
NS_INLINE void GCD_release_queue(dispatch_queue_t queue)
{
#if __has_feature(objc_arc)
#else
    dispatch_release(queue);
#endif
}

以下是创建一个单一线程的方法


#pragma mark - 在指定的线程池中执行该线程
NS_INLINE void GCD_dispatch_async(dispatch_queue_t queue, void (^block)())
{
    //在指定的线程池中执行操作
    dispatch_async(queue, ^{
        block();
    });
}

串行线程池

并发线程池

系统默认就有一个串行队列main_queue和并行队列global_queue,我对其进行了宏定义

// 系统子线程池(并发执行)
#define SYS_CONCURRENT_QUEUE_H  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
#define SYS_CONCURRENT_QUEUE_D  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
#define SYS_CONCURRENT_QUEUE_L  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
#define SYS_CONCURRENT_QUEUE_B  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)
// 系统主线程池(序列执行)
#define SYS_SERIAL_QUEUE      dispatch_get_main_queue()

所以,一般是这么用的,子线程处理数据,结束后把数据传递到主线程,让主线程来更新UI

GCD_dispatch_async(SYS_CONCURRENT_QUEUE_L, ^{
    // long-running task code here
    GCD_dispatch_async(SYS_SERIAL_QUEUE, ^{
        // update UI code here
    });
});

线程的延时操作

#pragma mark - [GCD] 执行某种线程池中的延时操作
NS_INLINE void GCD_DelaySeconds(int64_t seconds, dispatch_queue_t queue, void (^block)(dispatch_queue_t queue))
{
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC);
    dispatch_after(popTime, queue, ^(void){
        block(queue);
    });
}

 

线程组

#define SYS_CREATE_GROUP      dispatch_group_create()

#pragma mark - 线程组,用以监听所有的线程是否已经执行完毕了
NS_INLINE void GCD_dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, void (^block)())
{
    dispatch_group_async(group, queue, block);
}

 

以下是测试依次执行任务(线程)的代码以及结果,从现象以及结果可以看出,只有一个任务完整地执行完毕后才会执行下一个任务,这种线程池一次只处理一个任务

NS_INLINE NSData * dataFromNetUrlPath(NSString *path)
{
    //网络数据URL
    return [NSData dataWithContentsOfURL:[NSURL URLWithString:path]];
}

以下是测试并发执行任务(线程)的代码以及结果

即使是延时的操作都存在序列执行以及并发执行的区别,看结果

 

 group也存在并发非并发问题,如下所示

GCD定时器

#pragma mark - 创建定时器
NS_INLINE dispatch_source_t GCD_create_timer(int64_t seconds, dispatch_queue_t queue, void (^block)(dispatch_source_t timer))
{
    //创建Timer
    dispatch_source_t _timer =
    dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    //使用dispatch_source_set_timer函数设置timer参数
    dispatch_source_set_timer(_timer,
                              dispatch_time(DISPATCH_TIME_NOW, 0),
                              seconds * NSEC_PER_SEC,
                              0);
    
    //设置回调
    dispatch_source_set_event_handler(_timer, ^(){
                                          block(_timer);
                                      });
    
    //dispatch_source默认是Suspended状态,通过dispatch_resume函数开始它
    dispatch_resume(_timer);
    return _timer;
}

#pragma mark - 销毁定时器(记得释放定时器,ARC与非ARC都支持)
NS_INLINE void GCD_release_timer(dispatch_source_t timer)
{
#if __has_feature(objc_arc)
#else
    dispatch_release(timer);
#endif
}

 

 

GCD注意事项:

1. 选用了系统主线程池 dispatch_get_main_queue() 后,如果在这个线程池中执行了阻塞操作(例如执行了网络请求,但没有网络链接,网络超时),是会阻塞UI界面效果的,虽然,表面上看起来,启动了这个线程后就接着往下面执行了,这也说明了一个问题,UI控件的加载都是在主线程池中加载的,而主线程池本身就是串行线程池,所以,如果你写的代码在 dispatch_get_main_queue() 有着阻塞操作,会直接影响用户体验.

2. 同步线程 dispatch_sync 可以理解为一个代码块,必须执行完这个代码块之后才能往下面执行,但是,它不能在当前的线程池中执行,否则会造成死锁,要说用途,本人还没有研究出来^_^......

Calls to dispatch_sync() targeting the current queue will result in dead-lock

 

附录:

以下是本人自己封装的GCD类,支持ARC与非ARC,包含了GCD的queue,group以及semaphore,其属于初级封装,但见名知意.

提供源码以及使用方法如下

YXGCD.h

 

//
//  YXGCD.h
//
//  http://home.cnblogs.com/u/YouXianMing/
//
//  Created by YouXian on 14-4-9.
//  Copyright (c) 2014年 Y.X. All rights reserved.
//

#import <Foundation/Foundation.h>

// 系统子线程池(并发执行)
#define SYS_CONCURRENT_QUEUE_H  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
#define SYS_CONCURRENT_QUEUE_D  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
#define SYS_CONCURRENT_QUEUE_L  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
#define SYS_CONCURRENT_QUEUE_B  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)

// 系统主线程池(序列执行)
#define SYS_SERIAL_QUEUE        dispatch_get_main_queue()
#define SYS_UI_QUEUE            dispatch_get_main_queue()

//优先级枚举类型
typedef enum {
    
    HIGH_PRIORITY        = DISPATCH_QUEUE_PRIORITY_HIGH,
    LOW_PRIORITY         = DISPATCH_QUEUE_PRIORITY_LOW,
    DEFAULT_PRIORITY     = DISPATCH_QUEUE_PRIORITY_DEFAULT,
    BACKGROUND_PRIORITY  = DISPATCH_QUEUE_PRIORITY_BACKGROUND,
    
} QUEUE_PRIORITY;

@interface YXGCD : NSObject

#pragma mark - GCD_queue
+ (dispatch_queue_t)createSerialQueueWithName:(NSString *)name;
+ (dispatch_queue_t)createConcurrentQueueWithName:(NSString *)name;
+ (void)releaseQueue:(dispatch_queue_t)queue;
+ (void)asyncInQueue:(dispatch_queue_t)queue block:(void (^)())block;
+ (void)syncInQueue:(dispatch_queue_t)queue block:(void (^)())block;
+ (void)queue:(dispatch_queue_t)queue
 asyncInGroup:(dispatch_group_t)group
        block:(void (^)())block;
+ (void)queue:(dispatch_queue_t)queue
notifyInGroup:(dispatch_group_t)group
        block:(void (^)())block;

#pragma mark - GCD_group
+ (dispatch_group_t)createGroup;
+ (void)releaseGroup:(dispatch_group_t)group;
+ (void)enterGroup:(dispatch_group_t)group;
+ (void)leaveGroup:(dispatch_group_t)group;
+ (void)waitGroup:(dispatch_group_t)group;
+ (void)explicitlyIndicatesGroup:(dispatch_group_t)group
                    asyncInQueue:(dispatch_queue_t)queue
                           block:(void (^)())block;
+ (void)waitGroup:(dispatch_group_t)group
     asyncInQueue:(dispatch_queue_t)queue
           before:(void (^)())before
            after:(void (^)())after;
+ (void)waitGroup:(dispatch_group_t)group
          forTime:(int64_t)time
     asyncInQueue:(dispatch_queue_t)queue
           before:(void (^)())before
            after:(void (^)(long result))after;

#pragma mark - GCD_semaphore
+ (dispatch_semaphore_t)createSemaphoreWithValue:(long)value;
+ (void)releaseSemaphore:(dispatch_semaphore_t)semaphore;
+ (void)signalForSemaphore:(dispatch_semaphore_t)semaphore;
+ (void)waitForSemaphore:(dispatch_semaphore_t)semaphore;
+ (void)waitForSemaphore:(dispatch_semaphore_t)semaphore forTime:(int64_t)time;

@end

 

YXGCD.m

 

//
//  YXGCD.m
//
//  http://home.cnblogs.com/u/YouXianMing/
//
//  Created by YouXian on 14-4-9.
//  Copyright (c) 2014年 Y.X. All rights reserved.
//

#import "YXGCD.h"

@implementation YXGCD

+ (void)asyncInQueue:(dispatch_queue_t)queue block:(void (^)())block
{
    dispatch_async(queue, ^{
        block();
    });
}

+ (void)syncInQueue:(dispatch_queue_t)queue block:(void (^)())block
{
    dispatch_sync(queue, ^{
        block();
    });
}

+ (void)asyncInBackgroundWithPriority:(QUEUE_PRIORITY)type block:(void (^)())block
{
    dispatch_async(dispatch_get_global_queue(type, 0), ^{
        block();
    });
}

+ (dispatch_queue_t)createSerialQueueWithName:(NSString *)name
{
    dispatch_queue_t serialQ
    = dispatch_queue_create([name cStringUsingEncoding:NSUTF8StringEncoding],
                            DISPATCH_QUEUE_SERIAL);
    
    return serialQ;
}

+ (dispatch_queue_t) createConcurrentQueueWithName:(NSString *)name
{
    dispatch_queue_t concurrentQ
    = dispatch_queue_create([name cStringUsingEncoding:NSUTF8StringEncoding],
                            DISPATCH_QUEUE_CONCURRENT);
    
    return concurrentQ;
}

+ (void)asyncInGroup:(dispatch_group_t)group
             inQueue:(dispatch_queue_t)queue
               block:(void (^)())block
{
    dispatch_group_async(group, queue, block);
}

+ (void)queue:(dispatch_queue_t)queue
 asyncInGroup:(dispatch_group_t)group
        block:(void (^)())block
{
    dispatch_group_async(group, queue, block);
}

+ (void)queue:(dispatch_queue_t)queue
notifyInGroup:(dispatch_group_t)group
        block:(void (^)())block
{
    dispatch_group_notify(group, queue, ^{
        block();
    });
}

+ (dispatch_group_t)createGroup
{
    return dispatch_group_create();
}

+ (void)enterGroup:(dispatch_group_t)group
{
    dispatch_group_enter(group);
}

+ (void)leaveGroup:(dispatch_group_t)group
{
    dispatch_group_leave(group);
}

+ (void)waitGroup:(dispatch_group_t)group
{
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}

+ (void)explicitlyIndicatesGroup:(dispatch_group_t)group
                    asyncInQueue:(dispatch_queue_t)queue
                           block:(void (^)())block
{
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        block();
        dispatch_group_leave(group);
    });
}

+ (void)waitGroup:(dispatch_group_t)group
     asyncInQueue:(dispatch_queue_t)queue
           before:(void (^)())before
            after:(void (^)())after
{
    dispatch_async(queue, ^{
        before();
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        after();
    });
}

+ (void)waitGroup:(dispatch_group_t)group
          forTime:(int64_t)time
     asyncInQueue:(dispatch_queue_t)queue
           before:(void (^)())before
            after:(void (^)(long result))after
{
    dispatch_async(queue, ^{
        before();
        long result = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, time));
        after(result);
    });
}

+ (dispatch_semaphore_t)createSemaphoreWithValue:(long)value
{
    return dispatch_semaphore_create(value);
}

+ (void)signalForSemaphore:(dispatch_semaphore_t)semaphore
{
    dispatch_semaphore_signal(semaphore);
}

+ (void)waitForSemaphore:(dispatch_semaphore_t)semaphore
{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}

+ (void)waitForSemaphore:(dispatch_semaphore_t)semaphore forTime:(int64_t)time
{
    dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, time));
}

+ (void)releaseSemaphore:(dispatch_semaphore_t)semaphore
{
#if __has_feature(objc_arc)
#else
    dispatch_release(semaphore);
#endif
}

+ (void)releaseGroup:(dispatch_group_t)group
{
#if __has_feature(objc_arc)
#else
    dispatch_release(group);
#endif
}

+ (void)releaseQueue:(dispatch_queue_t)queue
{
#if __has_feature(objc_arc)
#else
    dispatch_release(queue);
#endif
}

@end

用法:

在主线程中操作

    [YXGCD asyncInQueue:SYS_UI_QUEUE block:^{
        // 更新UI操作代码
    }];

在子线程中操作
    [YXGCD asyncInQueue:SYS_CONCURRENT_QUEUE_D block:^{
        // 阻塞操作
    }];

子线程阻塞操作,主线程更新UI
[YXGCD asyncInQueue:SYS_CONCURRENT_QUEUE_D block:^{
        // 阻塞操作
        
        [YXGCD asyncInQueue:SYS_UI_QUEUE block:^{
            // 更新UI操作
        }];
    }];

创建一个串行线程池,执行完后释放线程池(英文解释的含义是,当所有的任务执行完毕后,才会销毁线程池)
/*
     When your application no longer needs the dispatch queue, it should release
     it with the dispatch_release function. Any pending blocks submitted to a
     queue hold a reference to that queue, so the queue is not deallocated until
     all pending blocks have completed.
     */
    
    // 创建一个序列线程
    dispatch_queue_t serialQueue = [YXGCD createSerialQueueWithName:@"Y.X."];
    
    // 序列任务1
    [YXGCD asyncInQueue:serialQueue block:^{
        // 你的代码
    }];
    
    // 序列任务2
    [YXGCD asyncInQueue:serialQueue block:^{
        // 你的代码
    }];
    
    // 释放线程池
    [YXGCD releaseQueue:serialQueue];

使用GCD-group实现线程的等待操作
// 创建一个GCD-group
    dispatch_group_t group = [YXGCD createGroup];
    
    // 系统主线程的一个任务,加入到了监听组中
    [YXGCD queue:SYS_UI_QUEUE asyncInGroup:group block:^{
        // 代码
    }];
    
    // 系统默认优先级的一个子线程任务,加入到了监听组中
    [YXGCD queue:SYS_CONCURRENT_QUEUE_D asyncInGroup:group block:^{
        // 代码
    }];
    
    // 系统默认优先级的一个子线程,监听上面的任务执行完毕后,才会执行任务
    [YXGCD queue:SYS_CONCURRENT_QUEUE_D notifyInGroup:group block:^{
        // 代码
    }];
    
    // 释放GCD-group
    [YXGCD releaseGroup:group];

另外一种实现线程等待的方式
// 创建一个GCD-group
    dispatch_group_t group = [YXGCD createGroup];
    
    // 在系统默认子线程中执行代码,注意enterGroup与leaveGroup必须成对出现
    [YXGCD enterGroup:group];
    [YXGCD asyncInQueue:SYS_CONCURRENT_QUEUE_D block:^{
        // 代码
        
        [YXGCD leaveGroup:group];
    }];
    
    // 在系统主线程中执行代码,注意enterGroup与leaveGroup必须成对出现
    [YXGCD enterGroup:group];
    [YXGCD asyncInQueue:SYS_UI_QUEUE block:^{
        // 代码
        
        [YXGCD leaveGroup:group];
    }];
    
    // 在系统默认子线程中执行代码,等待上面的线程执行完毕
    [YXGCD asyncInQueue:SYS_CONCURRENT_QUEUE_D block:^{
        [YXGCD waitGroup:group];
        // 上面两个线程执行完毕且leaveGroup后才会执行下面的代码
    }];

    // 释放线程组
    [YXGCD releaseGroup:group];

封装了group的enter与leave操作,更简单易用
// 创建一个GCD-group
    dispatch_group_t group = [YXGCD createGroup];
    
    // 封装group的enter与leave操作
    [YXGCD explicitlyIndicatesGroup:group asyncInQueue:SYS_UI_QUEUE block:^{
        // 代码
    }];

    // 封装group的enter与leave操作
    [YXGCD explicitlyIndicatesGroup:group asyncInQueue:SYS_CONCURRENT_QUEUE_D block:^{
        // 代码
    }];
    
    [YXGCD waitGroup:group asyncInQueue:SYS_CONCURRENT_QUEUE_D before:^{
        // wait之前的操作
    } after:^{
        // 等到所有在组中的任务结束后,就会执行
    }];

    // 释放线程组
    [YXGCD releaseGroup:group];

具有超时等待特性的group操作
// 创建一个GCD-group
    dispatch_group_t group = [YXGCD createGroup];
    
    // 封装group的enter与leave操作
    [YXGCD explicitlyIndicatesGroup:group asyncInQueue:SYS_UI_QUEUE block:^{
        // 代码
    }];

    // 封装group的enter与leave操作
    [YXGCD explicitlyIndicatesGroup:group asyncInQueue:SYS_CONCURRENT_QUEUE_D block:^{
        // 代码
    }];
    
    // 等待3s,看所有组中的线程是否已经完成
    [YXGCD waitGroup:group forTime:3 * NSEC_PER_SEC asyncInQueue:SYS_UI_QUEUE before:^{
        
    } after:^(long result) {
        if (result == 0)
        {
            NSLog(@"所有线程都完成了");
        }
        else
        {
            NSLog(@"有的线程没有完成,超时了");
        }
    }];

    // 释放线程组
    [YXGCD releaseGroup:group];

GCD信号量用法
// 创建一个信号量
    dispatch_semaphore_t semaphore = [YXGCD createSemaphoreWithValue:0];
    
    [YXGCD asyncInQueue:SYS_CONCURRENT_QUEUE_D block:^{
        // 连续5次发送信号量
        for (int i = 0; i < 5; i++)
        {
            // 模拟阻塞操作1s
            sleep(1);
            
            // 发送信号量
            [YXGCD signalForSemaphore:semaphore];
        }
    }];
    
    [YXGCD asyncInQueue:SYS_CONCURRENT_QUEUE_D block:^{
        // 死循环,不停等待接收信号量
        while (1)
        {
            // 阻塞等待接收信号量
            [YXGCD waitForSemaphore:semaphore];
            
            // 你的操作
            NSLog(@"Y.X.");
        }

    }];
    
    // 释放信号量
    [YXGCD releaseSemaphore:semaphore];


目录
相关文章
|
7月前
|
API 调度 iOS开发
多线程和异步编程:什么是 GCD(Grand Central Dispatch)?如何在 iOS 中使用 GCD?
多线程和异步编程:什么是 GCD(Grand Central Dispatch)?如何在 iOS 中使用 GCD?
81 1
|
前端开发 API
patch使用
+ put:对所有资源进行更新 + patch:对部分资源进行更新 put使用方法和post相同,但是put是幂等的。
|
算法
LeetCode 330. Patching Array
给定一个已排序的正整数数组 nums,和一个正整数 n 。从 [1, n] 区间内选取任意个数字补充到 nums 中,使得 [1, n] 区间内的任何数字都可以用 nums 中某几个数字的和来表示。请输出满足上述要求的最少需要补充的数字个数。
79 0
LeetCode 330. Patching Array
|
开发工具 git
生成patch
生成patch
GCD全解-dispatch_after/dispatch_time-t延迟操作
GCD全解-dispatch_after/dispatch_time-t延迟操作
345 0
|
监控
Dispatch Source 应用
Dispatch Source 源是一个偏底层的函数集合,使用时CPU负荷非常小,尽量不占资源,开发过程中大多是配合定时器使用。
199 0
|
Android开发
一分钟学会使用9-patch
背景 之前项目开发一般都是写逻辑,UI方面的操作用的比较少。 图片也是直接使用设计师提供的。 因此一般情况下不需要用到9-patch图。 然而,最近项目中分配给我的功能用到了。
1566 0