Objective-C中GCD

Posted Jk_Chan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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死锁了
    • });
    • });
    • });

 

以上是关于Objective-C中GCD的主要内容,如果未能解决你的问题,请参考以下文章

Objective-C中的Hello World

在objective-c中使用swift代码时的问题

Objective-C中GCD

在 Objective-C 中使用异常

C++和Objective-C混编(官方文档翻译)

iOS中Objective-C已死Swift当立?