iOS与OS多线程和内存管理---GCD的API详解

来源:互联网 发布:mac怎么安装搜狗输入法 编辑:程序博客网 时间:2024/05/22 00:17

本文是学习《Objective-C高级编程:iOS与OS多线程和内存管理》的总结,详细的介绍了dispatch_queue_creat、Main Dispatch Queue/Global Dispatch Queue、dispatch_set_target_queue、dispatch_after、Dispatch Group、dispatch_apply、dispatch–sync(async)、 dispatch_suspend/dispatch_resume以及Dispatch Semaphore,并且编写了例子练习并验证。
Demo:https://github.com/onebutterflyW/GCD.git
3.2.2 dispatch_queue_create—第一种获取Dispatch Queue方法

使用dispatch_queue_create可创建Dispatch Queue,但有以下说明。
(1)关于Serial Dispatch Queue创建个数的问题,我们知道Concurrent Dispatch Queue可并行执行多个追加处理,Serial Dispatch Queue只能执行1个追加处理,但是使用dispatch_queue_create可以创键多个Serial Dispatch Queue,此时各个Serial Dispatch Queue将比性处理,因为每个Serial Dispatch Queue生成并追加处理,系统就会生成一条线程。这样的话多个Serial Dispatch Queue就是并行处理。所以Serial Dispatch Queue的生成量应该仅限于所必须的数量。
影响,过多的使用多线程,会大量消耗内存,引起大量的上下文切换,降低系统性能
使用Serial Dispatch Queue;在多线程更新相同的资源导致数据竞争时使用Serial Dispatch Queue
(2)该函数第一个参数指定生成线程的名字,退浆使用app ID的逆序;第二个参数当生成Serial Dipatch Queue时设置为NULL,当生成Concurrent Dispatch Queue时设置为Dispatch_QUEUE_CONCURRENT
(3)生成的Dispatch Queue必须由程序员释放,因为它没有像block那样作为OC对象来处理,所以使用完生成的对象,要使用dispatch_release来释放,使用dispatch_reatain持有,在GCD的几个API中含有create的函数在不需要生成对象的时候要使用release函数释放,但是实际上在使用ARC后,使用dispatch_release后用error。这一点不清楚,书上说的在什么情况下使用

3.2.3 Main Dispatch Queue/Global Dispatch Queue—第二种获取Dispatch Queue方法
(1)Main Dispatch Queue是在主线程中执行的Dispatch Queue,所以是Serial Dispatch Queue,追加到Main Dispatch Queue中的处理会在主线程的RunLoop中指向,所以可以将更新用户界面的代码加入到Main Dispatch Queue中,这与NSObject类得 performSelectorOnMainThread实例方法相同。
(2)Global Dispatch Queue是所有应用程序都可以使用的Concurrent Dispatch Queue,Global Dispatch Queue有四个优先级,分别是High Priority、Default priority、Low priority以及background priority,使用优先级是XNU的内核处理的,并不能保证实时性,只是大致判断优先级;在设置了优先级后,XNU内核管理的用于Global Dispatch Queue的线程,将按照设置的优先级作为线程执行的优先级。

  //获取主线程    dispatch_queue_t mainQueue = dispatch_get_main_queue();    //获取各个优先级的Global Dispatch Queue    dispatch_queue_t highGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);    dispatch_queue_t defaultGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    dispatch_queue_t lowGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);    dispatch_queue_t backGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

3.2.4 dispatch_set_target_queue
(1)使用dispatch_set_target_queue更改Dispatch Queue的执行优先级
dispatch_queue_create函数生成的Dispatch Queue不管是Serial Dispatch Queue还是Concurrent Dispatch Queue,执行的优先级都与默认优先级的Global Dispatch queue相同,如果需要变更生成的Dispatch Queue的执行优先级则需要使用dispatch_set_target_queue函数

 - (void)testTeagerQueue1 {     dispatch_queue_t serialQueue = dispatch_queue_create("com.GCD.www",NULL);     dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);     dispatch_set_target_queue(serialQueue, globalQueue);     // 第一个参数为要设置优先级的queue,第二个参数是参照物,既将第一个queue的优先级和第二个queue的优先级设置一样。 }

(2)使用dispatch_set_target_queue修改用户队列的目标队列,使多个serial queue在目标queue上一次只有一个执行

    1   -(void)testTargetQueue {      2       dispatch_queue_t targetQueue = dispatch_queue_create("com.GCD.www", DISPATCH_QUEUE_SERIAL);      3             4             5             6       dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);      7       dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);      8       dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);      9             10      dispatch_set_target_queue(queue1, targetQueue);      11      dispatch_set_target_queue(queue2, targetQueue);      12      dispatch_set_target_queue(queue3, targetQueue);      13            14            15      dispatch_async(queue1, ^{      16          NSLog(@"1 in");      17          [NSThread sleepForTimeInterval:3.f];      18          NSLog(@"1 out");      19      });      20        21      dispatch_async(queue2, ^{      22          NSLog(@"2 in");      23          [NSThread sleepForTimeInterval:2.f];      24          NSLog(@"2 out");      25      });      26      dispatch_async(queue3, ^{      27          NSLog(@"3 in");      28          [NSThread sleepForTimeInterval:1.f];      29          NSLog(@"3 out");      30      });      31            32            33            34  }  

3.2.5 dispatch_after
使用场景:想在指定的时间后执行某些处理的情况,可使用dispatch_after
下面是在3秒后将指定的Block追加到Main Dispatch Queue中的代码
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull*NSEC_PER_SEC);

    dispatch_after(time, dispatch_get_main_queue(), ^{        NSLog(@"使用dispatch_after,3秒后追加操作");    });

注意:dispatch_after不是在3秒后执行block追加的处理,而是在指定的时间后追加处理。此代码和3秒后使用dispatch_async函数追加到Main Dispatch Queue相同

3.2.6 Dispatch Group
(1)使用场景,有一个需求:在追加到Dispatch Queue中的的多个处理全部处理完毕后,想执行结束处理。如果是Serial Dispatch Queue时比较简单,将多个处理追加到同一个Serial Dispatch Queue中,在最后追加结束处理即可,但如果使用Concurrent Dispatch Queue时,因为多个处理是并行执行的就不好处理了,此时可以使用Dispatch Group实现。

   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    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, queue, ^{        NSLog(@"done");    });

输出
2017-03-02 10:32:13.715 GCD[872:400958] blk1
2017-03-02 10:32:13.715 GCD[872:400960] blk2
2017-03-02 10:32:13.715 GCD[872:400959] blk0
2017-03-02 10:32:13.715 GCD[872:400959] done

总结:无论是向Serial Dispatch Queue或者Concurrent Dispatch Queue追加多个处理,都可使用Dispatch Group实现多个追加处理执行完毕,再追加结束处理到Dispatch Queue中。

(2)书上说明,使用完毕Dispatch Group后使用dispatch_release释放,但实际上在ARC下添加它出错。

(3)dispatch_group_wait函数,等待(这里的等待的意思是在dispatch_group_wait指定的时间或属于Dispatch Group的处理全部执行完之前,执行该函数的线程是停止的)全部处理执行完,返回值如果为0说明,追加的全部处理执行完,如果不为0,因为着虽然过了指定的时间(第二个参数指定时间),但属于Dispatch Group的某个处理还在执行。当第二个参数为DISPATCH_TIME_FOREVER时,意味着永久等待,他的返回值恒为0;如果是DISPATCH_TIME_NOW,则不用等待即可判定属于Dispatch Group的处理是否结束。检查属于Dispatch Group的处理是否结束是在主线程的Runloop的每次循环中判断的,不需要等待多余的时间

   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    dispatch_group_t group = dispatch_group_create();    dispatch_group_async(group, queue, ^{NSLog(@"blk0");});    dispatch_group_async(group, queue, ^{        for (int i = 0; i < 400; i ++) {            NSLog(@"blk1");        }    });    dispatch_group_async(group, queue, ^{NSLog(@"blk2");});    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);  long result  = dispatch_group_wait(group, time);    if (result == 0) {         NSLog(@" dispatch_group_wait==0:%@",[NSThread currentThread]);    }else{        NSLog(@" dispatch_group_wait!=0:%@",[NSThread currentThread]);    }

3.2.7dispatch_barrier_async

官方:一个dispatch barrier 允许在一个并发队列中创建一个同步点。调用这个函数总是在barrier block被提交之后立即返回,不会等到block被执行。当在并发队列中遇到一个barrier, 他会延迟执行barrier的block(barrier block已经到并发队列的最前端,他不会立即执行),会等待所有在barrier之前提交的blocks执行结束,barrier block才开始执行。 所有在barrier block之后提交的blocks会等到barrier block结束之后才执行。
使用dispatch_barrier_async和Concurrent Dispatch Queue一起使用可实现高效率的数据库访问和文件访问。写入处理不可与其他写入处理或读取处理并行执行,但是读取处理与其他读取处理并行执行不会发生问题。即,为了高效访问,将读取处理追加到Concurrent Dispatch Queue中,写入处理在任一个读取处理没有执行的状态下,追加到Serial Dispatch Queue中即可。

    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.GCD.www", DISPATCH_QUEUE_CONCURRENT);    dispatch_async(concurrentQueue, ^(){        NSLog(@"dispatch-1");    });    dispatch_async(concurrentQueue, ^(){        NSLog(@"dispatch-2");    });    dispatch_barrier_async(concurrentQueue, ^(){        NSLog(@"dispatch-barrier");    });    dispatch_async(concurrentQueue, ^(){        NSLog(@"dispatch-3");    });    dispatch_async(concurrentQueue, ^(){        NSLog(@"dispatch-4");    });    dispatch_async(concurrentQueue, ^(){        NSLog(@"dispatch-5");    });

输出
2017-03-02 11:10:44.948 GCD[979:505579] dispatch-2
2017-03-02 11:10:44.948 GCD[979:505580] dispatch-1
2017-03-02 11:10:44.948 GCD[979:505580] dispatch-barrier
2017-03-02 11:10:44.948 GCD[979:505580] dispatch-3
2017-03-02 11:10:44.948 GCD[979:505579] dispatch-4
2017-03-02 11:10:44.948 GCD[979:505581] dispatch-5

3.2.8 dispatch_sync

dispatch_sync是同步添加操作,它是等待添加进队列里的操作完成之后再继续执行
dispatch_async是异步添加操作,它不做任何等待,将操作添加到队列中立即返回继续执行
队列有四种:自己创建的Serial Dispatch queue、Concurrent Dispatch Queue、Main Dispatch Queue以及Global Dispatch queue;所以就会有八种组合
1、dispatch_sync不会创建新线程,添加的任务在主线程中串行执行
(1)dispatch_sync与自己创建的Serial Dispatch queue,不会创键新的线程,并且添加的任务在主线程中串行执行
(2)dispatch_sync自己创建的Concurrent Dispatch Queue,不会创键新的线程,并且添加的任务在主线程中串行执行
(3)dispatch_sync与Main Dispatch Queue会产生死锁,
(4)dispatch_sync与Global Dispatch queue不会创键新的线程,并且添加的任务在主线程中串行执行

2、dispatch_async
(1)dispatch_async与自己创建的Serial Dispatch queue,dispatch_async添加任务说明有开启新线程的能力,Serial Dispatch queue,说明XXNU内核会开启一个线程,但添加的任务串行执行
(2)dispatch_async与自己创建的Concurrent Dispatch queue,dispatch_async添加任务说明有开启新线程的能力,Concurrent Dispatch queue队列说明XXNU内核会创建多个新的线程务并行执行添加的任务
(3)dispatch_async与Main Dispatch Queue,因为任务在主队列中,所以不会创建新线程,任务会主线程中串行执行
(4)dispatch_async与Global Dispatch queue,dispatch_async添加任务说明有开启新线程的能力;使用Global Dispatch queue队列,XNU内核创建多个新的线程,添加的任务并行执行

Demo:

3.2.9 dispatch_apply

dispatch_apply:按指定的次数将指定的block追加到指定的Dispatch Queue中,并等待全部处理执行结束;相当于Dispatch Group和Dispatch_sync的关联API。(这里说与Dispatch_sync有关是因为dispatch_apply函数会等待添加的任务执行结束,效果上相当于Dispatch_sync;所以推荐在Dispatch_async中非同步的使用dispatch_apply);其实dispatch_apply可以使用Dispatch Group中的dispatch_group_notify来实现.

   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    dispatch_apply(10, queue, ^(size_t index) {        NSLog(@"%zu",index);    });    NSLog(@“done");

输出:
2017-03-02 13:30:36.959 GCD[1123:836861] 0
2017-03-02 13:30:36.959 GCD[1123:837355] 2
2017-03-02 13:30:36.959 GCD[1123:837332] 1
2017-03-02 13:30:36.959 GCD[1123:837374] 3
2017-03-02 13:30:36.959 GCD[1123:836861] 4
2017-03-02 13:30:36.959 GCD[1123:837355] 5
2017-03-02 13:30:36.960 GCD[1123:836861] 7
2017-03-02 13:30:36.960 GCD[1123:837332] 6
2017-03-02 13:30:36.960 GCD[1123:837374] 8
2017-03-02 13:30:36.960 GCD[1123:837355] 9
2017-03-02 13:30:36.960 GCD[1123:836861] done

使用Dispatch Group中的dispatch_group_notify来实现上面代码的类似功能

   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    dispatch_group_t group = dispatch_group_create();    dispatch_group_async(group, queue, ^{        NSLog(@"1");    });    dispatch_group_async(group, queue, ^{        NSLog(@"2");    });    dispatch_group_async(group, queue, ^{        NSLog(@"3");    });    dispatch_group_async(group, queue, ^{        NSLog(@"4");    });    dispatch_group_async(group, queue, ^{        NSLog(@"5");    });    dispatch_group_async(group, queue, ^{        NSLog(@"6");    });    dispatch_group_async(group, queue, ^{        NSLog(@"7");    });    dispatch_group_async(group, queue, ^{        NSLog(@"8");    });    dispatch_group_async(group, queue, ^{        NSLog(@"9");    });    dispatch_group_async(group, queue, ^{        NSLog(@"10");    });    dispatch_group_notify(group, dispatch_get_main_queue(), ^{        NSLog(@"done");    });

输出
2017-03-02 13:25:53.679 GCD[1108:817573] 3
2017-03-02 13:25:53.679 GCD[1108:817580] 1
2017-03-02 13:25:53.679 GCD[1108:817586] 2
2017-03-02 13:25:53.679 GCD[1108:817608] 4
2017-03-02 13:25:53.679 GCD[1108:817609] 5
2017-03-02 13:25:53.679 GCD[1108:817573] 6
2017-03-02 13:25:53.680 GCD[1108:817580] 7
2017-03-02 13:25:53.680 GCD[1108:817586] 8
2017-03-02 13:25:53.680 GCD[1108:817608] 9
2017-03-02 13:25:53.680 GCD[1108:817609] 10
2017-03-02 13:25:53.682 GCD[1108:816657] done

3.2.10 dispatch_suspend/dispatch_resume
(1)dispatch_suspend挂起指定的线程
(2)dispatch_resume回复指定的线程
两个个函数对已经执行的处理无影响,主音箱尚未处理的操作

3.2.11 Dispatch Semaphore

信号量是一个整形值并且具有一个初始计数值,并且支持两个操作:信号通知和等待。当一个信号量被信号通知,其计数会被增加。当一个线程在一个信号量上等待时,线程会被阻塞(如果有必要的话),直至计数器大于零,然后线程会减少这个计数。
  在GCD中有三个函数是semaphore的操作,分别是:
  dispatch_semaphore_create   创建一个semaphore
  dispatch_semaphore_signal   发送一个信号
  dispatch_semaphore_wait    等待信号
  简单的介绍一下这三个函数,第一个函数有一个整形的参数,我们可以理解为信号的总量,dispatch_semaphore_signal是发送一个信号,自然会让信号总量加1,dispatch_semaphore_wait等待信号,可以正常的执行,并让信号总量-1,当信号总量少于0的时候就会一直等待。根据这样的原理,我们便可以快速的创建一个并发控制来同步任务和有限资源访问控制。

    1   dispatch_group_t group = dispatch_group_create();       2       dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);       3       dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);       4       for (int i = 0; i < 100; i++)       5       {       6           dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);       7           dispatch_group_async(group, queue, ^{       8               NSLog(@"%i",i);       9               sleep(2);       10              dispatch_semaphore_signal(semaphore);       11          });       12      }       13      dispatch_group_wait(group, DISPATCH_TIME_FOREVER);       14      dispatch_release(group);       15      dispatch_release(semaphore);  

 简单的介绍一下这一段代码,创建了一个初使值为10的semaphore,每一次for循环都会创建一个新的线程,线程结束的时候会发送一个信号,线程创建之前会信号等待,所以当同时创建了10个线程之后,for循环就会阻塞,等待有线程结束之后会增加一个信号才继续执行,如此就形成了对并发的控制,如上就是一个并发数为10的一个线程队列。

3.2.12 dispatch_once
dispatch_once保证指定的代码只执行一次,多用于单例模式
使用模式

static dispatch_once_t pres;dispatch_once(&prers,^{/*初始化*/});
0 0