Objective-C中GCD

  1. 同步
  2. 异步
  3. 并行
  4. 串行
  5. 任务组
  6. 时间等待

Dispatch Queue有两种:

  • 1.Serial Dispatch Queue,串行Queue,按队列顺序每次只能执行一个该线程中追加的任务(可通过创建多个串行queue实现并行执行任务(会降低性能))

    • 串行queue可解决多个线程更新相同资源导致数据竞争的问题,让操作该资源的任务放在同一个串行queue中执行即可
  • 2.Concurrent Dispatch Queue,并行Queue,该线程中追加的任务可同时执行

获取Dispatch Queue(dispatch_queue_t类型)对象有两种方法:

  • 1.通过C函数dispatch_queue_create("queueName",NULL);

    • 当第二个参数为NULL时,创建的queue为串行queue
    • 当第二个参数为DISPATCH_QUEUE_CONCURRENT宏时,创建的queue为并行queue
    • dispatch_queue_carete(或其他GCD API中包含create的函数生成的对象)生成的queue需要自己调用dispatch_release(dispatch_queue_t变量)释放
    • dispatch_async(queue变量,^{往queue中追加的任务体block})函数会让block持有该queue,所以可以在该函数后即时调用dispatch_release(queue变量)函数释放queue,block执行完后ARC会主动调用dispatch_release(queue变量)函数释放queue,
    • dispatch_retain函数可让变量持有queue
  • 2.通过获取系统标准提供的Main Dispatch Queue(主线程,串行)或Global Dispatch Queue(并行,有4个执行优先级:高DISPATCH_QUEUE_PRIORITY_HIGH,默认DISPATCH_QUEUE_PRIORITY_DEFAULT,低DISPATCH_QUEUE_PRIORITY_LOW,后台DISPATCH_QUEUE_PRIORITY_BACKGROUND)
    • 获取Main Dispatch Queue如:dispatch_queue_t mainDispatchQueue=dispatch_get_main_queue();
    • 获取Global Dispatch Queue如:dispatch_queue_t globalDispatchQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
    • 第一个参数为指定该queue的执行优先级

变更由dispatch_queue_create函数生成的dispatch_queue_t对象的执行优先级的方法:

  • 通过调用dispatch_set_target_queue(需变更的queue对象变量,要变更成该queue一样优先级的queue对象变量)函数
  • 众queue中,只有Global Dispatch Queue可以在获取时直接指定优先级,所以dispatch_set_target_queue函数第二个参数可以以Global Dispatch Queue对象变量作为入参去变更第一个参数的queue执行优先级

限制Dispatch Queue的执行阶层的方法:

  • 也是通过调用dispatch_set_target_queue(一般是限制串行目标queue1,在限制对象为串行queue2)函数

想在指定时间后把block体任务追加到线程中(指定时间执行任务,也不一定绝对是指定时间后就执行该任务,因为只是指定时间后把任务加到线程中,可能线程还有别的事情做而延迟)的方法:

  • 通过调用dispatch_after(dispatch_time_t对象变量,dispatch_queue_t对象变量,^{追加的任务block体})函数
  • 第一个参数通过dispatch_time(开始时间如DISPATCH_TIME_NOW,延迟多长时间如3ull*NSEC_PER_SEC)函数获取dispatch_time_t对象
  • dispatch_time(,)函数通常用户计算相对时间,还有一个dispatch_walltime()函数用于获取绝对时间(略)

想在多个处理(使用并行queue或同时执行多个串行queue时)全部结束后(在未全部执行结束前不会让调用执行的线程挂起等待)执行指定操作的方法(可用于任务间的依赖关系):

  • 通过Dispatch Group任务组来追加管理众任务,具体做法:

    • 1.往线程追加block体任务时指定该任务所属任务组(各任务block体所追加到的线程不一定需要在同一条线程中,但是需要是同一个任务组中),通过调用dispatch_group_async(dispatch_grooup_t对象变量,任务追加到的执行的线程queue,^{block任务体})函数多次调用多次追加任务到任务组中

      • 第一个参数通过dispatch_group_create()函数获取dispatch_group_t对象
    • 2.然后通过调用dispatch_group_notify(dispatch_grooup_t对象变量,结束任务执行的线程queue不一定是和任务组中的其他任务线程一样,^{block任务体})函数追加任务组中全部任务执行完后的任务
    • 3.最后要释放由dispatch_group_create()函数获取dispatch_group_t对象,通过调用dispatch_release(dispatch_group_t对象变量)释放

想等待任务组中各任务全部任务执行完毕的方法(在未全部执行结束前会让调用执行的线程挂起等待)
通过Dispatch Group任务组来追加管理众任务,具体做法:

  • 1.往线程追加block体任务时指定该任务所属任务组,通过调用dispatch_group_async(dispatch_grooup_t对象变量,任务追加到的执行的线程queue,^{block任务体})函数多次调用多次追加任务到任务组中

    • 第一个参数通过dispatch_group_create()函数获取dispatch_group_t对象
  • 2.然后通过调用dispatch_group_wait(dispatch_grooup_t对象变量,dispatch_time_t对象变量等待时间或宏DISPATCH_TIME_FOREVER永远等)函数等待任务组中任务执行完毕(中途不能取消)
    • dispatch_group_wait(,)函数返回值==0代表等待时间内任务组中的任务全部执行完毕,不然就是超过等待的时间后任务组中的某一个处理还在执行中
    • 调用dispatch_group_wait(,)函数的线程会在调用dispatch_group_wait(,)后便开始停止,经过等待时间后或任务组中任务全部执行完毕后才继续往下执行
  • 3.最后要释放由dispatch_group_create()函数获取dispatch_group_t对象,通过调用dispatch_release(dispatch_group_t对象变量)释放

想读取文件或操作数据库时更高效率不单止所有操作仅交给一条串行线程负责,而是所有读写操作并行执行,写入操作保证在任一个读取处理没有执行状态下执行,并且执行写入操作未结束前读取操作不可执行,高效有效处理资源竞争问题:

  • 通过调用dispatch_barrier_async/dispatch_barrier_sync函数结合dispatch_queue_create函数生成的并行queue一起使用实现,栏栅追加也就是dispatch_barrier_async/dispatch_barrier_sync只针对并行queue使用,具体做法:

    • 1.dispatch_queue_create(,)函数生成并行queue
    • 2.需要读取时调用dispatch_async(并行queue,^{读取任务block体})往并行queue中追加读取操作
    • 3.在需要写入操作时,调用dispatch_barrier_async/dispatch_barrier_sync(与读取操作同一条并行queue,^{写入任务block体})函数
    • 4.需要读取时调用dispatch_async(并行queue,^{读取任务block体})往并行queue中追加读取操作
    • (原理:并行队列如果发现接下来要处理的任务block体是由栏栅方式追加的,那么就一直等当前这并发线程中已经正在并发执行着的任务块都执行完,才单独执行这个栏栅任务block体,等这个栏栅块执行完后才再去并发执行该并发线程中剩余的任务体)

想按指定的次数将指定的任务block体追加到指定的dispatch_queue_t(并行或串行的queue都可以)中,并等待全部处理执行结束的方法:

  • 通过调用dispatch_apply(指定的追加次数,指定的并行线程,^(size_t index){指定的任务block体})函数实现
  • 当执行调用该函数的线程调用了该函数后,将会等待这批次的任务体执行完才往下执行。因此调用该函数的线程一半为子线程并且是在异步追加到该子线程的任务block体中调用
  • 不在子线程调用会卡界面,不是在异步追加的任务block体中调用的话会导致死锁

当线程执行大量操作想挂起线程或者恢复线程的方法:

  • 通过调用dispatch_suspend(dispatch_queue_t对象变量)函数可挂起指定的线程
  • 通过调用dispatch_resume(dispatch_queue_t对象变量)函数可恢复指定线程继续执行任务

想保证在引用程序执行期间只执行一次指定处理的方法:

  • 通过调用dispatch_once(&dispatch_once_t对象变量,^{指定处理的block体})函数实现
  • 其中第一个参数dispatch_once_t对象变量需要是静态的,如直接static dispatch_once_t token;把&token传入第一个参数即可
  • 通常用于创建单例类,如下语法:
    • +(id)sharedInstance{
    • static MyObject *myObjectManager=nil;
    • static dispatch_once_t onceToken;
    • dispatch_once(&onceToken,^{
    • myObjectManager=[[self alloc]init];
    • });
    • return myObjectManager;
    • }

想进行更细粒度的排他控制的方法:

  • 通过Dispathc Semaphore处理(略)

想提高文件读取速度的方法:

  • 通过Dispatch I/O处理(略)

往Dispatch Queue追加任务有两种方式:

  • 1.异步追加,调用C函数dispatch_async(dispatch_queue_t类型的queue变量,dispatch_block_t类型的block体^{往queue中追加的任务体block})

    • 任务追加到的线程不会等待所追加的任务block体执行结束
  • 2.同步追加,调用C函数dispatch_sync(dispatch_queue_t类型的queue变量,dispatch_block_t类型的block体^{往queue中追加的任务体block})
    • 任务追加到的线程会一直等待所追加的任务block体执行完成,是简易版的dispatch_group_wait函数
  • 同步追加容易引起死锁,如往主线程同步追加任务,因为代码执行时,主线程在执行往主线程同步追加任务,所以同步追加的任务主线程永远执行不到,又因是同步追加,所以主线程一直在等待追加的任务执行完毕,
  • 导致死锁,亦即无论何时,都不能往主线程或主线程执行的任务体中同步追加任务,或者把任务体同步追加到的线程永远不能是调用追加函数也就是调用dispatch_sync(,)函数时的线程及其线程的外部线程(见一下死锁例子)
  • 需同步等待例子如:
    • 有一条线程比如是主线程执行下面的代码
    • NSString *[email protected]"XXXXX";
    • -(NSString *)getaString{
    • dispatch_queue_t myQueue=dispatch_queue_creat("handleAStringQueue",NULL);
    • __block NSString *aString;//为解决多线程竞争使用_aString成员变量,所以对_aString操作都需放handleAStringQueue串行queue中操作,而在queue中不能直接返回_aString成员对象,因为在block中return只是block的返回值,所以需要声明一个替换变量
    • dispatch_sync(myQueue,^{aString=_aString});//主线程执行到这里的时候,由于是同步把block任务追加到myQueue中处理执行,所以主线程会挂起直到myQueue线程把追加的block任务体执行完才往下执行
    • return aString;//主线程执行到这里的时候由于上面是同步执行,所以block任务体一定已经执行完,所以aString已经被赋值,可以返回出去了。如果上面是异步追加任务的话,那么主线程会把block任务体追加到myQueue中,不等任务体给aString赋值马上执行return语句,这样是达不到原意的。
    • 导致死锁例子如:(表层死锁)
    • dispatch_sync(dispatch_get_main_queue(),^{
    • NSLog(@"content...");
    • });
    • 如果以上代码是在主线程运行,就会导致死锁。
    • 导致死锁例子如:(深层死锁)
    • dispatch_queue_t queueA=dispathc_queue_create("queueA Name",NULL);
    • dispatch_queue_t queueB=dispatch_queue_create("queueB Name",NULL);
    • //尽管是在另线程调用以下代码:
    • dispatch_sync(queueA,^{//在queueA中执行以下代码
    • dispatch_sync(queueB,^{//queueA等待queueB执行以下代码
    • dispatch_sync(queueA,^{//queueB等待queueA执行以下代码
    • NSLog(@"content...");//queueA永远执行不到这里,因为queueA上面同步等待挂起了,所以queueA死锁了
    • });
    • });
    • });
时间: 2024-10-12 04:43:00

Objective-C中GCD的相关文章

objective C中的字符串(三)

holydancer原创,如需转载,请在显要位置注明: 转自holydancer的CSDN专栏,原文地址:http://blog.csdn.net/holydancer/article/details/7343561 objective C中的字符串操作 在OC中创建字符串时,一般不使用C的方法,因为C将字符串作为字符数组,所以在操作时会有很多不方便的地方,在Cocoa中NSString集成的一些方法,可以很方便的操作字符串,下面举几个例子: 1.创建: 直接利用等号赋值 NSString *

iOS开发中GCD在多线程方面的理解

GCD为Grand Central Dispatch的缩写. Grand Central Dispatch (GCD)是Apple开发的一个多核编程的较新的解决方法.在Mac OS X 10.6雪豹中首次推出,并在最近引入到了iOS4.0. GCD是一个替代诸如NSThread等技术的很高效和强大的技术.GCD完全可以处理诸如数据锁定和资源泄漏等复杂的异步编程问题. GCD可以完成很多事情,但是这里仅关注在iOS应用中实现多线程所需的一些基础知识. 在开始之前,需要理解是要提供给GCD队列的是代

objective C中继承、协议、分类和多态的实现

第一.objective C中继承的实现 在oc中只有实例变量会有权限控制,实例方法和类方法是没有权限控制的,这点与c++不同,OC默认的是protected,并且在声明权限控制时,没有分号 在OC中可以像C++一样用指针运算法来访问实例变量 Rectangle.h 文件代码: #import <Foundation/Foundation.h> @interface Rectangle : NSObject { int _width; int _height; } @property (non

objective C中的字符串

holydancer原创,如需转载,请在显要位置注明: 转自holydancer的CSDN专栏,原文地址:http://blog.csdn.net/holydancer/article/details/7343561 objective C中的字符串操作 在OC中创建字符串时,一般不使用C的方法,因为C将字符串作为字符数组,所以在操作时会有很多不方便的地方,在Cocoa中NSString集成的一些方法,可以很方便的操作字符串,下面举几个例子: 1.创建: 直接利用等号赋值 NSString *

彻底搞懂OC中GCD导致死锁的原因和解决方案

GCD提供了功能强大的任务和队列控制功能,相比于NSOperationQueue更加底层,因此如果不注意也会导致死锁. 所谓死锁,通常指有两个线程A和B都卡住了,并等待对方完成某些操作.A不能完成是因为它在等待B完成.但B也不能完成,因为它在等待A完成.于是大家都完不成,就导致了死锁(DeadLock). 有一定GCD使用经验的新手通常认为,死锁是很高端的操作系统层面的问题,离我很远,一般不会遇上.其实这种想法是非常错误的,因为只要简单三行代码(如果愿意,甚至写在一行就可以)就可以人为创造出死锁

iOS中GCD的使用小结

本篇博客共分以下几个模块来介绍GCD的相关内容: 多线程相关概念 多线程编程技术的优缺点比较? GCD中的三种队列类型 The main queue(主线程串行队列) Global queue(全局并发队列) Custom queue (自定义队列) Group queue (队列组) GCD中一些系统提供的常用dispatch方法 多线程相关概念 进程与线程 进程概念: 进程是程序在计算机上的一次执行活动,打开一个app,就开启了一个进程,可包含多个线程. 线程概念: 独立执行的代码段,一个线

Objective C中定义可变参函数

Objective C中有很多不定参函数,例如NSLog(format, arg1, arg2),还有字符串或数组在构造时所用的[NSString stringWithFormat: format, arg1, arg2, arg3],它们的方法原型分别是: FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2); + (id)stringWithFormat:(NSString *)format,

Objective - C中属性和点语法的使用

一.属性 属性是Objective—C 2.0定义的语法,为实例变量提供了setter.getter方法的默认实现能在一定程度上简化程序代码,并且增强实例变量的访问安全性 OC中的属性机制提供了便捷的设置和获取实例变量的方式,或者说属性提供了一个默认的设置器和访问器的实现:属性提供的方法是现实可配置的,属性的好处:相当于自己编写这一对方法,属性提供了一系列清晰分明的说明参数来定义设置器和访问器的行为,编译器可以根据你设置的说明参数为你生成相应的方法,减少你的代码量和维护工作量 设置器(sette

Objective - C 中的KVC(一)(视图、便携、易懂、原创纯手打 定制版)

KVC的使用 1.KVC 全称 key valued coding 键值编码 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性.JAVA,C#都有这个机制.ObjC也有,所以你根部不必进行任何操作就可以进行属性的动态读写,就是KVC. KVC的操作方法由NSKeyValueCoding提供,而他是NSObject的类别,也就是说ObjC中几乎所有的对象都支持KVC操作. 2.常用方法 获取值的方法 valueForKey