GCD(Grand Center Dispatch)是异步执行任务的技术之一。开发者定义想执行的任务并且追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。
多线程编程一个CPU一次只能执行一个命令,不能执行某处分开的并列的两个命令,所以通过CPU执行的CPU命令列就好比一条无分叉的大道,其执行不会出现分歧。就像下图一样:
==这里所说的“一个CPU执行的CPU命令列为一条无分叉路径”就是线程。==同时存在多条这样的路径就称为“多线程”。
但是多线程编程时会出现很多问题,使用GCD大大简化了偏于复杂的多线程编程的源代码,并且可以避免多线程的一些缺点。
上面我们说到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
。
第一种方法是通过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
。
第二种方法就是获取系统标准提供的Dispatch Queue
。那就是Main Dispatch Queue
和Global 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 Queue
和Global Dispatch Queue
函数使用dispatch_retain和dispatch_release函数不会引起任何变化。
下面是使用Main Dispatch Queue
和Global Dispatch Queue
的源代码:
Fahaxiki:
[图片]
Fahaxiki:
/*
在默认优先级的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 Queue
和Global 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来实现。
比如在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 group
由dispatch_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
类型的值。
在访问数据库或者文件时,使用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_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_sync
函数和Dispatch Group
的关联API,该函数按照指定的次数将指定的Block追加到指定的Dispatch Queue
,并等待全部处理执行结束。
dispatch_apply(n, queue, ^() {
});
参数列表:
n为指定次数queue为指定Dispatch QueueBlock为指定处理 dispatch_suspend / dispatch_resumedispatch_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
函数是保证在应用程序执行中只执行一次指定处理的API。在之前我写的博客Manage封装一个网络请求中使用的就是这个函数来实现的。
static dispatch_once_t pred;
dispatch_once(&pred,^{
/*
初始化*/
));
单例模式就是通过此函数来实现的。
Dispatch I/O在读取较大文件时,如果将其分成合适大小并使用Global Dispatch Queue
并列读取的话,速度会快不少。实现这一功能的就是Dispatch I/O
和Dispatch Data
。
GCD的Dispatch Queue
非常方便,如何实现它呢?
首先确认一下实现Dispatch Queue
而使用的软件组件:
Dispatch Source是BSD系内核惯有功能kqueue的包装。kqueue是在XNU内核中发生各种事件时,在应用程序编程方执行处理的技术。其CPU符合非常小,尽量不占用资源。
Dispatch Source可处理以下事件:
Dispatch Source与Dispatch Queue的不同是,后者可以将追加的执行处理取消,而前者则不能。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)