【iOS开发】—— GCD

GCD概述

什么是GCD?

GCD(Grand Center Dispatch)是异步执行任务的技术之一。开发者定义想执行的任务并且追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。

多线程编程

一个CPU一次只能执行一个命令,不能执行某处分开的并列的两个命令,所以通过CPU执行的CPU命令列就好比一条无分叉的大道,其执行不会出现分歧。就像下图一样:
在这里插入图片描述
==这里所说的“一个CPU执行的CPU命令列为一条无分叉路径”就是线程。==同时存在多条这样的路径就称为“多线程”。
但是多线程编程时会出现很多问题,使用GCD大大简化了偏于复杂的多线程编程的源代码,并且可以避免多线程的一些缺点。

GCD的API

Dispatch Queue

上面我们说到GCD是:开发者定义想执行的任务并且追加到适当的Dispatch Queue中。这句话可以用下面这段源代码表示:

dispatch_async (queue, ^{
	//想执行的任务
}

该源代码使用Block语法“定义想执行的任务”,通过dispatch_async函数“追加”赋值在变量queue的“Dispatch Queue”中。仅这样就可使指定的Block在另一线程中执行。

Dispatch Queue是什么呢?是执行处理的等待队列,按照追加任务的顺序(先进先出)执行处理。如下图:
在这里插入图片描述
在执行处理时,存在两种Dispatch Queue,一种是等待现在执行中处理的Serial Dispatch Queue,另一种是不等待现在执行中处理的Concurrent Dispatch Queue。二者执行情况如下图:
在这里插入图片描述
二者和线程的关系:
在这里插入图片描述
在执行Concurrent Dispatch Queue中执行处理时,执行顺序会根据处理内容和系统状态发生改变。而Serial Dispatch Queue则顺序固定。

既然知道了有两种Dispatch Queue,那么如何去创建呢?有以下两种方法:dispatch_queue_create和获取系统标准提供的Dispatch Queue

dispatch_queue_create

第一种方法是通过GCD的API生成Dispatch Queue,通过dispatch_queue_create函数可生成Dispatch Queue

dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);

参数列表:
第一个参数指定Serial Dispatch Queue的名称,命名推荐使用应用程序ID这种逆序全程域名。
第二个参数:生成Serial Dispatch Queue类时,则指定为NULL,生成Concurrent Dispatch Queue时,指定为DISPATCH_QUEUE_CONCURRENT
说明:使用dispatch_queue_create函数可以生成任意多个Dispatch Queue,如果过多使用会消耗大量内存。但是使用多个Serial Dispatch Queue可以避免多线程编程引起的多个线程对数据的竞争:
请添加图片描述
dispatch_queue_create的返回值为dispatch_queue_t类型。

生成的Dispatch Queue必须由程序员手动释放。

dispatch_release(queue);

有release,则可推断出相应地存在dispatch_retain函数,通过这两个函数的引用计数来管理内存。

在这里插入图片描述
这里立即通过dispatch_release释放是没有问题的,原因:在dispatch_async中追加Block到Dispatch Queue,也就是说,该Block通过dispatch_retain函数持有Dispatch Queue,即使立即释放Dispatch Queue,该Dispatch Queue由于被Block所持有也不会被释放,一旦Block执行结束后,这时谁都不再持有Dispatch Queue,就通过dispatch_release函数释放该block持有的Dispatch Queue

Main Dispatch Queue/Global Dispatch Queue

第二种方法就是获取系统标准提供的Dispatch Queue。那就是Main Dispatch QueueGlobal Dispatch Queue

Main Dispatch Queue是在主线程中执行的Dispatch Queue,因主线程只有一个,所以Main Dispatch Queue就是Serial Dispatch Queue。追加到Main Dispatch Queue中的处理是在主线程的RunLoop中执行的,因此要将用户界面的界面更新等一些必须在主线程中执行的处理追加到Main Dispatch Queue使用。
在这里插入图片描述
另一个Global Dispatch Queue是所有应用程序都能够使用的Concurrent Dispatch Queue。其有四个优先级:高优先级、默认优先级、低优先级和后台优先级。
关于Dispatch Queue总结如下:
请添加图片描述
各种Dispatch Queue的获取方法:

//Main Dispatch Queue 的获取方法
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();

//Global Dispatch Queue(高优先级)的获取方法
dispatch queue_t globalDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY HIGH, 0);

//Global Dispatch Queue(默认优先级)的获取
dispatch queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//Global Dispatch Queue(低优先级)的获取方法
dispatch queue_t globalDispatchQueueLow = dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0);

//Global Dispatch Queue(后台优先级)的获取方法
dispatch queue t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0) ;

注意:对于Main Dispatch QueueGlobal Dispatch Queue函数使用dispatch_retain和dispatch_release函数不会引起任何变化。

下面是使用Main Dispatch QueueGlobal Dispatch Queue的源代码:

/*
在默认优先级的Global Dispatch Queue中执行Block
*/
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT,O)^{
/*
*可并行执行的处理*/
/*
*在Main Dispatch Queue中执行Block
*/
dispatch_async (dispatch_get_main_queue()^{
		/*只能在主线程中执行的处理*/
	}) ;
}) ;

dispatch_set_target_queue

dispatch_queue_t_create函数生成的Dispatch Queue,都使用与默认与默认优先级Global Dispatch Queue相同执行优先级的线程。可以使用 dispatch_set_target_queue函数变更执行优先级。
该函数一共有两个参数:
第一个是要变更执行优先级的Dispatch Queue。但是不可以是系统提供的Main Dispatch QueueGlobal Dispatch Queue
第二个是要使用的执行优先级相同优先级的Dispatch Queue

使用dispatch_set_target_queue,还可以改变Dispatch Queue的执行阶层,比如:有多个Serial Dispatch Queue,若dispatch_set_target_queue的指定目标为其中某一个,则那么原先本来应该并行执行多个Serial Dispatch Queue,在目标Serial Dispatch Queue上只能同时执行一个处理;所以使用此函数可以预防处理的并行执行
请添加图片描述

dispatch_after

想在指定时间后执行处理的情况,可使用dispatch_after来实现。
比如在3秒后将指定的Block追加到Main Dispatch Queue中的源代码:

dispatch_time_t time =dispatch_time(DISPATCH_TIME_NOW, 3u11 * NSEC_PER_SEC);
dispatch after(time, dispatch_get_main_queue()^{
	NSLog(@"waited at least three seconds.");
}) ;

注意:这不是指在3秒后执行处理,而是指在3秒后追加处理到Dispatch Queue

参数列表:

  • 第一个:是指定时间用的dispatch_time_t类型的值,该值使用dispatch_time函数或者dispatch_walltime函数作成。
  • 第二个:指定要追加处理的Dispatch Queue
  • 第三个:指定记述要处理的Block

Dispatch Group

dispatch groupdispatch_group_create函数生成的dispatch_group_t类型的。使用结束需要通过dispatch_release释放。
下面举例介绍一下dispatch group的用法:

dispatch_queue_t queue =
	dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,O); 
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group,queue:^{NSLog(@"blk0");}); 
dispatch_group_async(group,queue:^{NSLog(@"blk1");}); dispatch_group_async(group,queue:^{NSLog(@"blk2");});
dispatch_group_notify(group,
	dispatch_get_main_queue():^{NSLog(@"done");}); dispatch_release(group);

在Dispatch Group中也可以使用dispatch_group_wait函数仅等待全部执行结束:

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

第二个参数指定为等待的时间,它属于dispatch_time_t类型的值。

dispatch_barrier_async

在访问数据库或者文件时,使用Serial Dispatch Queue可避免数据竞争的问题。

为了高效的进行访问,可以将读取处理追加到Concurrent Dispatch Queue中,写入处理在任一个读取处理没有执行的状态下,追加到Serial Dispatch Queue中即可(在写入处理结束之前,读取处理不可执行)。
dispatch_barrier_async函数会等待追加到Concurrent Dispatch Queue上的并行执行的处理全部结束之后,再将指定的处理追加到该Concurrent Dispatch Queue上。然后在由dispatch_barrier_async函数追加的处理执行完毕之后,Concurrent Dispatch Queue才恢复一般的动作。
dispatch_barrier_async的使用方法特别简单,用它代替dispatch_async,它与dispatch_async使用方法是一样的。
在这里插入图片描述

dispatch_sync

dispatch_async函数的“async”意味着“非同步”,就是将指定的Block“非同步”地追加到指定的Dispatch Queue中。dispatch_async函数不做任何等待。
在这里插入图片描述
相反dispatch_sync函数,它意味着“同步”,也就是将指定的Block“同步”追加到指定的Dispatch Queue中。在追加结束之前,dispatch_sync函数会一直等待。
在这里插入图片描述
(图片中为dispatch_sync)。

一旦调用dispatch_sync函数,那么在指定的Block处理执行结束之前,该函数不会返回。所以极易造成死锁。比如在主线程中调用该函数等。

dispatch_barrier_async函数中含有async可推测出,相应地也有dispatch_barrier_sync函数。dispatch_barrier_sync函数的作用是在等待追加的处理全部执行结束后,再追加处理到Dispatch Queue中,此外,它还会等待追加处理的执行结束。

dispatch_apply

dispatch_apply函数是dispatch_sync函数和Dispatch Group的关联API,该函数按照指定的次数将指定的Block追加到指定的Dispatch Queue,并等待全部处理执行结束。

dispatch_apply(n, queue, ^() {
	
}); 

参数列表:

  • n为指定次数
  • queue为指定Dispatch Queue
  • Block为指定处理

dispatch_suspend / dispatch_resume

dispatch_suspend函数用于挂起不希望执行的Dispatch Queue

dispatch_suspend(queue);

dispatch_resume函数用于恢复被挂起的Dispatch Queue

dispatch_resume(queue)

Dispatch Semaphore

Dispatch Semaphore是持有计数的信号,该计数是多线程编程中的计数类型信号。计数为0时等待,计数为1或者大于1时,计数减一而不等待。
使用方法:
使用dispatch_semaphore_create函数生成dispatch_semaphore_t类型的Dispatch Semaphore

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

参数表示的是计数的初始值。
该函数课通过dispatch_retain持有,通过dispatch_release释放。

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

dispatch_semaphore_wait函数等待Dispatch Semaphore的计数值达到1或者等于1。当计数值大于等于1,或者在待机中计数值大于等于1时,对该计数进行减法并从dispatch_semaphore_wait函数返回。可通过函数返回值进行达到一些希望的目的和操作。

dispatch_time_t time =dispatch_time( DISPATCH_TIME_NOW, 1U11 * NSEC_PER_SEC); 
long result = dispatch_semaphore_wait(semaphore, time);
if(result ==0){
/*
由于Dispatch Semaphore的计数值达到大于等于1或者在待机中的指定时间内
* Dispatch Semaphore的计数值达到大于等于1
*所以Dispatch Semaphore的计数值减去1。
*可执行需要进行排他控制的处理*/
} else {
/*
*由于Dispatch Semaphore的计数值为0*因此在达到指定时间为止待机
*/
}

dispatch_semaphore_wait函数返回0时,可安全地执行需要进行排他控制的处理。该处理结束时通过dispatch_semaphore_signal函数将Dispatch Semaphore的计数值加1。

dispatch_once

dispatch_once函数是保证在应用程序执行中只执行一次指定处理的API。在之前我写的博客Manage封装一个网络请求中使用的就是这个函数来实现的。

static dispatch_once_t pred;
dispatch_once(&pred,^{
/*
初始化*/
));

单例模式就是通过此函数来实现的。

Dispatch I/O

在读取较大文件时,如果将其分成合适大小并使用Global Dispatch Queue并列读取的话,速度会快不少。实现这一功能的就是Dispatch I/ODispatch Data

GCD的实现

Dispatch Queue

GCD的Dispatch Queue非常方便,如何实现它呢?
首先确认一下实现Dispatch Queue而使用的软件组件:
请添加图片描述

Dispatch Source

Dispatch Source是BSD系内核惯有功能kqueue的包装。kqueue是在XNU内核中发生各种事件时,在应用程序编程方执行处理的技术。其CPU符合非常小,尽量不占用资源。
Dispatch Source可处理以下事件:
请添加图片描述

Dispatch Source与Dispatch Queue的不同是,后者可以将追加的执行处理取消,而前者则不能。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值