GCD的讨论

本文重在讨论苹果公司在IOS4引入的全新多线程编程技术——GCD。一个人理解来说一说GCD。

1. 引入GCD

​ 首先在学习GCD之前,一定要对线程有一定的理解,例如,线程等待,死锁,线程同步等等,如需了解请点击【线程的介绍】。另外GCD是一中与Block有关的技术,因此还需要学习一下Block,请点击【IOS的Block

​ 好了,下面进入正文。GCD全称是"Grand Central Dispatch",译为大中枢派发(来自《Effective Objective-C》)。GCD对线程进行了抽象,开发者只需要定义想要执行的任务(Block)并追加到合适的任务队列(Dispatch Queue)中,GCD就能生成必要的线程并执行任务。这里要着重的注意:任务(Block) + 任务队列(Dispatch Queue)。GCD用一种非常简洁的方式来实现了负责的多线程编程。

很多IOS的资料都会推荐使用GCD,因为使用GCD会带来如下好处:

  • GCD 可用于多核的并行运算;
  • GCD 会自动利用更多的 CPU 内核(比如双核、四核);
  • GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程);
  • 程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码。

上述的好处会在后面的介绍中一一体现。

2. GCD的任务与队列

2.1 任务

​ 任务就是要执行的Block,其类型为dispatch_block_t。如果查看定义其实一个无返回值无参数的Block:typedef void (^dispatch_block_t)(void);

2.2 队列

​ 队列就是数据结构中满足先进先出(FIFO)特性的队列,不过该队列中存储的数据是代码块,其类型为dispatch_queue_t。任务队列分为两种:

  1. DISPATCH_QUEUE_SERIAL,串行队列

  2. DISPATCH_QUEUE_CONCURRENT,并行队列

    对于上述两种队列。如果对于串行和并行有一定了解的话,就能明白。串行队列中,任务的执行是按部就班的一个一个的执行;并行队列中,任务的执行是根据当前系统的状况,创建新线程或使用空闲线程来取到队列中的任务并执行。因此串行队列中就只有一个线程来执行任务,而并行队列中会有多个线程来并行执行。

ps:并行是一个相对的概念,如果当前系统不是多核的,那么就不支持同一时间执行多个任务,因此就不支持并行
在这里插入图片描述

3. GCD的基本使用

基本的介绍就在前面结束,下面通过介绍API来进一步了解和使用。

3.1 创建队列
dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr);

第一个参数是队列的名字,通常采用公司域名的逆置,例如:com.xxx.xxx.queue或者团队的逆置名。第二个参数是队列的类型DISPATCH_QUEUE_CONCURRENTDISPATCH_QUEUE_SERIAL。返回值类型为dispatch_queue_t,即就是返回一个队列

3.2 任务的追加
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

async是asynchronous的简写,译为非同步,反之就是同步sync。第一个参数是任务队列,第二个参数是要执行的Block

  1. dispatch_sync,往一个派发队列上提交一个同步执行的Block,阻塞当前线程.不开新线程.

  2. dispatch_async,往一个派发队列上提交一个异步执行的Block,不阻塞当前线程.可能会开新线程.

3.3 队列的获取
dispatch_get_main_queue()

获取主线程队列。对于串行队列,GCD 默认提供了主队列(Main Dispatch Queue)。主队列其实并不特殊。 主队列的实质上就是一个普通的串行队列,只是因为默认情况下,当前代码是放在主队列中的,然后主队列中的代码,又都会放到主线程中去执行,所以才造成了主队列特殊的现象

dispatch_get_global_queue(long identifier, unsigned long flags);

获取全局队列。对于并行队列,GCD 默认提供了全局并行队列(Global Dispatch Queue)。第一个参数表示队列优先级,一般用 DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数为保留字段备用(一般为0)

3.3 组合

GCD的基本使用,这里不再介绍,因为看到前面的API就可以使用了。这里讨论一下注意事项。首先,前面提到队列有两种队列,如果加上后面GCD默认提供的队列则有 4 种,但是全局并行队列可以作为普通并行队列来使用。由于当前代码默认放在主队列中,在使用中,会有差异,所以主队列很有必要专门来研究一下。由于追加又分为异步追加和同步追加,因此总共有 6 中组合方式。

  1. 同步执行 + 并行队列
  2. 异步执行 + 并行队列
  3. 同步执行 + 串行队列
  4. 异步执行 + 串行队列

dispatch_sync 提交的Block因为优化的原因几乎总是在当前线程执行的(注意是当前线程),也就是说在哪个线程调用的这个方法,那么该Block就在哪个线程上执行,唯一的一个例外就是如果在子线程中调用并提交到主队列则block是在主线程执行。之所以说这个,是因为同步执行在某些情况下会导致死锁就是这个原因。

3.3.1 同步执行 + 并行队列

在当前这一个线程中执行任务,不具有派发的能力,当前线程执行完一个任务,再执行下一个任务。

任务按顺序执行的。按顺序执行的原因:虽然 并行队列 可以开启多个线程,并且同时执行多个任务。但是因为本身不能创建新线程,只有当前线程这一个线程(同步任务 不具备开启新线程的能力),所以也就不存在并行。而且当前线程只有等待当前队列中正在执行的任务执行完毕之后,才能继续接着执行下面的操作(同步任务 需要等待队列的任务执行结束)。所以任务只能一个接一个按顺序执行,不能同时被执行。

3.3.2 异步执行 + 并行队列

异步执行 具备开启新线程的能力。且 并行队列 可开启多个线程,同时执行多个任务

3.3.3 同步执行 + 串行队列

同步执行 不具备开启新线程的能力,串行队列 每次只有一个任务被执行,任务一个接一个按顺序执行。此时就会带来问题。如在串行队列中取到一个block中,有一个执行了sync,追加了一个block2,且追加到当前的queue。那么sync会阻塞当前线程,直到block2执行完毕。但是线程被阻塞,block2永远不会被执行,因此死锁了。
概括一下就是:在当前queue上调用sync函数,sync指定的queue也是当前queue。需要执行的block被放到当前queue的队尾等待被执行,因为这是一个串行的queue,调用sync函数会阻塞当前队列,等待block被执行->这个block一直不会被执行->sync函数一直不返回,所以当前queue就被阻塞了,造成了死锁。
在这里插入图片描述

3.3.4 异步执行 + 串行队列

异步执行 具备开启新线程的能力,串行队列 只开启一个线程,串行队列 每次只有一个任务被执行,任务一个接一个按顺序执行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值