GCD概念总结

GCD基本词汇:
queue 队列
task 任务
synchronize 同步的
asynchronize 异步的
serial 连续的,串行的
concurrent 同时的,并发的
global 全局的
serial dispatch queue 串行队列
concurrent dispatch queue 并发队列
main dispatch queue 主队列
global dispatch queue 全局队列

Functions (函数)
创建和管理队列的函数:
dispatch_get_mian_queue 获取主队列
dispatch_get_global_queue 获取全局队列
dispatch_queue_create 创建队列
dispatch_queue_current_queue获取当前队列

调度任务的函数:
dispatch_async 异步调度队列中的任务
dispatch_sync 同步调度队列中的任务
dispatch_after 延迟执行
dispatch_apply 多次执行某个任务
dispatch_once 一次性执行

调度组函数:
dispatch_group_async 将block提交到队列,异步执行
dispatch_group_create 创建调度组
dispatch_group_enter 进入调度组
dispatch_group_leave 离开调度组
dispatch_group_notify 调度组通知
dispatch_group_wait 调度组等待

Data Types (数据类型)
dispatch_block_t block
dispatch_functions_t 函数
dispatch_group_t 调度组
dispatch_object_t "对象"
dispatch_once_t 一次性执行
dispatch_queue_t 队列
dispatch_time_t 时间

GCD :Grand Central Dispatch 很厉害的中枢调节器
纯c语言的,提供了非常强大的函数
Grand是宏伟的,极重要的意思

GCD的优势
GCD是苹果公司专门为多核的并行运算提出的解决方案
GCD会自动利用更多的cpu内核(比如双核,四核)
GCD会自动管理线程的生命周期(创建任务,调度任务,销毁线程)
程序员只需要告诉GCD要执行什么任务,不需要编写任何线程管理的代码

GCD的核心概念
将任务添加到队列,并且指定执行任务的函数
3个关键概念:
1.任务:执行什么代码
在GCD中,任务是通过block来封装的,并且任务的block没有参数也没有返回值
2.队列:用来存放任务
包括:串行队列,并发队列,主队列,全局队列
3.执行任务的函数:
1>同步:dispatch_sync queue 队列
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t, block);
2>异步
void dispatch_async(dispatch_queue_t queue ,dispatch_block_t block);

GCD的使用步骤:
第一步:创建/获取队列
第二步:创建任务,确定要做的事情
第三步:将任务添加到队列中
1. GCD会自动将对列中的任务取出,放到对应的线程中执行
2. 任务的取出遵循队列的FIFO(first in , first out)原则:先进先出, 后进后出

GCD基本演练
第一步: 获取全局队列
// 1. 获取全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
第二步: 创建任务
// 2. 创建任务
dispatch_block_t task = ^ {
NSLog(@"hello %@", [NSThread currentThread]);//current:当前的
};
第三步: 将任务添加到队列,并指定执行任务的函数
// 3. 将任务添加到队列,并且指定执行任务的函数
dispatch_async(queue, task);//第一个参数为队列,第二个参数为任务
===========================================================
(2) 精简代码
gcdDemo2:
- (void)gcdDemo2 {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"hello %@", [NSThread currentThread]);
});
}
同步函数:任务会在当前线程中执行,因为同步函数不具备开新线程的能力
异步函数:任务会在子线程中执行,因为异步函数具备新线程的能力

与 NSThread相比:
所有代码写在一起,让代码更加简单,易于阅读和维护
(1)1.NSThread 通过@selector 执行要执行的方法
2.GCD通过block执行要执行的代码,指定要执行的方法

(2)使用GCD不需要管理线程的创建/销毁/复用的过程,程序员不用管理线程的生命周期
如果要开多个线程,NSThread必须实例化多个线程对象

GCD队列的演练
1.串行队列(Serial Dispatch Queue)

串行队列的特点:
(1)以先进先出的方式,按顺序调度队列中的任务去执行,一次只能调度一个任务.
(2)无论队列中所指定的执行任务的函数是同步还是异步,都必须等待前一个任务执行完毕,才可以调度后面的任务.
串行对列创建:
(1)dispatch_queue_t queue = dispatch_queue_create("组名". DISPATCH_QUEUE_SERIAL);
(2)dispatch_queue_t queue = dispatch_queue_create("组名",NULL);

1.1串行队列,同步执行
 //创建串行队列
dispatch_queue_t queue = dispatch_queue_create("组名",DISPATCH_QUEUE_SERIAL);
//将任务添加到队列,并且制定同步执行
dispatch_sync(queue, ^{
                    NSLog(@"%@ %d",[NSThread currentThread], );
 });
不开启线程,顺序执行

1.2串行队列,异步执行

//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("组名",DISPATCH_QUEUE_SERIAL);
//将任务添加到队列,并且制定异步执行
dispatch_async(queue, ^{

                    NSLog(@"%@ %d",[NSThread currentThread], );
 });
 只会开一条线程,顺序执行
串行队列,不管同步还是异步执行,都按顺序执行,原因:串行队列特点是先进先出

2.并发队列(ConcurrentDispatch Queue)
并发队列的特点:
(1)以先进先出的方式,并发(同时)调度队列中的任务去执行
(2)如果当前调度的任务是同步执行,会等待当前任务执行完毕后,再调度后续的任务
(3)如果当前调度的任务是异步执行的,同时底层线程池有可用的线程资源,就不会等待当前任务.直接调度额任务到新线程去执行.
并发队列的创建:
dispatch_queue_t queue = dispatch_queue_creat("组名",DISPATCH_QUEUE_CONCURRENT);
2.1并发队列,同步执行
//1.创建并发队列
dispatch_queue_t queue = dispatch_queue_creat("组名",DISPATCH_QUEUE_CONCURRENT);
//2.将任务添加到队列,并且制定同步执行
dispatch_sync(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread],i);
});
不开线程,顺序执行
2.2并发队列,异步执行
//1.创建并发队列
dispatch_queue_t queue = ("组名",DISAPTCH_QUEUE_CONCURRENT);
//2.将任务添加到队列,并且制定异步执行
dispatch_async(queue, ^{
});
开多条线程,乱序执行

3.主队列
主队列是系统提供的,无需自己创建,可以直接通过dispatch_get_main_queue( )函数来获取
主队列的特点:
(1)添加到主队列的任务只能由主线程来执行
(2)以先进先出的方式,只有当主线程的代码执行完毕后,主队列才会调度任务到主线程执行.
3.1 主队列,异步执行

  • (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self gcdDemo1];
    }

pragma mark - 主队列, 异步执行

// 在主线程顺序执行, 不开线程
// 主队列的特点: 只有当 主线程空闲时, 主队列才会调度任务到主线程执行

  • (void)gcdDemo1 {
    // 1. 获取主队列
    dispatch_queue_t q = dispatch_get_main_queue();

    // 2. 将任务添加到主队列, 并且指定异步执行
    for (int i = 0; i < 10; i++) {
    dispatch_async(q, ^{
    NSLog(@"%@ %d", [NSThread currentThread], i);
    });
    }

    // 先执行完这句代码, 才会执行主队列中的任务
    NSLog(@"hello %@", [NSThread currentThread]);
    }
    主队列,就算是异步执行,也不会开线程,顺序执行
    3.2 主队列,同步执行(死锁)

pragma mark - 主队列, 同步执行

// 主队列, 同步执行, 会死锁

  • (void)gcdDemo2 {

    NSLog(@"begin");
    //解决方法

    dispatch_async(dispatch_get_global_queue(0, 0),^{

// 1. 获取主队列
dispatch_queue_t q = dispatch_get_main_queue();

// 2. 将任务添加到主队列, 并且指定同步执行
// 死锁
for (int i = 0; i < 10; i++) {
    dispatch_sync(q, ^{
        NSLog(@"%@ %d", [NSThread currentThread], i);
    });
}

});

NSLog(@"end");

}
在主线程执行,主队列同步执行任务,会发生死锁
主线程和主队列同步任务相互等待,造成死锁

4.全局队列
全局队列是系统提供的,无需自己创建,可以直接通过dispatch_get_global_queue(long identifier, unsigned long flags);函数来获取
  • (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self gcdDemo];
    }
  • (void)gcdDemo {
    // 1. 获取全局队列
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    // 2. 将任务添加到全局队列, 异步执行
    for (int i = 0; i < 10; i++) {
    dispatch_async(q, ^{
    NSLog(@"%d %@", i, [NSThread currentThread]);
    });
    }
    1.全局对列的工作特点跟并发队列一致.实际上,全局队列就是系统为了方便程序员,专门提供的一种特殊的并发队列.
    2.全局队列和并发队列的区别
    1>全局队列没有名称
    无论ARC还是MRC都不需要考虑内存释放
    2>并发队列,有名称
    如果在MRC开发中,需要使用dispatch_release来释放相应的对象
    dispatch_barrier 必须使用自定义的并发队列
    开发第三方框架,建议使用并发队列
    3.dispatch_get_global_queue(long identifier, unsigned long flags);
    这个函数有两个参数

第一个参数: identifier
iOS7.0,表示的是优先级:
DISPATCH_QUEUE_PRIORITY_HIGH = 2; 高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT = 0; 默认优先级
DISPATCH_QUEUE_PRIORITY_LOW = -2; 低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND = INT16_MIN; 后台优先级
iOS8.0开始,推荐使用服务质量(QOS):
QOS_CLASS_USER_INTERACTIVE = 0x21; 用户交互
QOS_CLASS_USER_INITIATED = 0x19; 用户期望
QOS_CLASS_DEFAULT = 0x15; 默认
QOS_CLASS_UTILITY = 0x11; 实用工具
QOS_CLASS_BACKGROUND = 0x09; 后台
QOS_CLASS_UNSPECIFIED = 0x00; 未指定
通过对比可知: 第一个参数传入0,可以同时适配iOS7及iOS7以后的版本。
服务质量和优先级是一一对应的:
DISPATCH_QUEUE_PRIORITY_HIGH: QOS_CLASS_USER_INITIATED
DISPATCH_QUEUE_PRIORITY_DEFAULT: QOS_CLASS_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW: QOS_CLASS_UTILITY
DISPATCH_QUEUE_PRIORITY_BACKGROUND: QOS_CLASS_BACKGROUND
第二个参数: flags
为未来保留使用的,始终传入0。
Reserved for future use.

总结:

(1) 开不开线程,由执行任务的函数决定
1> 同步执行不开线程
2> 异步执行开线程
(2) 异步执行任务,开几条线程由队列决定
1> 串行队列,只会开一条线程,因为一条就足够了
2> 并发队列,可以开多条线程,具体开几条由线程池决定

在多线程开发中,真正有用的是 并发队列,异步执行,所以应该把注意力放在并发队列异步执行任务上面,其它的仅作了解即可。
六. 同步任务
同步任务的作用:网络开发中,通常有些任务执行会彼此依赖,这个时候就需要同步任务
开发中,经常会遇到类似情况:某些操作之间是有依赖关系的。只有登录了才可以付费,只有付费了才可以下载应用。这些操作必须按顺序执行。
通过同步执行任务,可以实现多个任务之间的依赖关系。
登录、付费、下载应用都需要联网操作,都属于耗时操作,应该放到子线程去执行。因此,这里使用到了 dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 联网操作; };

七、dispatch_barrier(阻塞)

1、barrier的作用

(1) 主要用于在多个异步操作完成之后,统一对非线程安全的对象进行更新。
(2) 适合于大规模的I/O操作。
(3) 当访问数据库或文件的时候,更新数据的时候不能和其它更新或读取的操作在同一时间执行,可以使用 dispatch_barrier_async解决。

八、dispatch_after延迟执行

pragma mark - 延迟执行

/**
从现在开始,经过多少纳秒,由队列调度执行block中的代码
参数:

  1. dispatch_time_t: when 从现在开始, 经过多少纳秒(即延迟多少时间)
  2. dispatch_queue_t: queue 队列
  3. dispatch_block_t: block 任务
    */
    九、dispatch_once 一次性执行

有的时候,在程序开发中,有些代码只想从程序启动就只执行一次,典型的应用场景就是”单例”。

  • (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self once];
    }

pragma mark - 一次性执行

/// 一次性执行的原理: 判断静态全局变量的值, 默认是0, 如果执行完成后, 就设置为-1
/// once 内部会判断 onceToken 的值, 如果是0才执行

  • (void)once {
    static dispatch_once_t onceToken;
    // 如果 onceToken == 0 就执行 block 中的代码
    NSLog(@"%zd", onceToken);

    for (int i = 0; i < 10; i++) {
    dispatch_once(&onceToken, ^{
    NSLog(@"是一次吗?");
    });
    }

    NSLog(@"%zd", onceToken);
    }
    十、单例

在日常开发中,经常会有一些工具类,在整个项目中都会用到,这个时候通常会将这个工具类封装成一个单例。因为单例在整个程序中始终只会存在一份,不会出现重复创建。
模拟创建一个网络工具类的单例(NetworkTools),并且继承自NSObject。
这里我们通过两种方式来创建单例: 互斥锁 和 dispatch_once, 并且会比较两种方式的性能。
第一步: 创建网络工具类 NetworkTools,继承自 NSObject。
第二步: 提供供全局访问的接口方法
NetworkTools.h
/// 使用互斥锁创建单例

  • (instancetype)sharedNetworkTools;

/// 使用 dispatch_once 创建单例
/// 全局的访问方法: 通常都是以 shared 开头

  • (instancetype)sharedNetworkToolsOnce;
    十一
    dispatch_group_enter 和 dispatch_group_leave 必须成对出现。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 背景 担心了两周的我终于轮到去医院做胃镜检查了!去的时候我都想好了最坏的可能(胃癌),之前在网上查的症状都很相似。...
    Dely阅读 9,226评论 21 42
  • 从哪说起呢? 单纯讲多线程编程真的不知道从哪下嘴。。 不如我直接引用一个最简单的问题,以这个作为切入点好了 在ma...
    Mr_Baymax阅读 2,731评论 1 17
  • NSThread 第一种:通过NSThread的对象方法 NSThread *thread = [[NSThrea...
    攻城狮GG阅读 789评论 0 3
  • 难得休息,多睡了半小时。 醒来,看着手机微信群里出现的小红点提醒,心情有些莫名烦躁,没有开灯,摸着黑随意打几行字,...
    维扬之水阅读 189评论 0 1
  • 我终是没有照顾好你, 这一朵风干的花。 你垂丧在墙角, 不吐芬芳。 我终是没有照顾好你, 这一头乌黑的发。 你被时...
    玉生烟阅读 171评论 5 6