OC中GCD使用

作者: CodeFXQ | 来源:发表于2017-02-21 17:48 被阅读61次

    一、GCD的使用:

    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

    async表明异步运行,block代表的是你要做的事情,queue则是你把任务交给谁来处理了.当程序异步执行时,如果是并发队列执行那么到底开辟多少线程有系统决定,如果是在同步队列执行的话只会开启一条子线程。

    之所以程序中会用到多线程是因为程序往往会需要下载数据,然后更新UI.为了良好的用户体验,读取数据的操作会倾向于在后台运行,这样以避免阻塞主线程.

    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

    sync表明同步运行,block代表的是你要做的事情,queue则是你把任务交给谁来处理了.需要注意的是,同步执行时无论是在dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);(全局并发队列还是自己创建的并发队列)系统都不会帮我们开启子线程,所有操作都是在主队列执行。但切记不能在dispatch_get_main_queue()队列执行,否则会造成死锁,这时候系统不知道应该先执行上面的操作,还是先执行下面的操作。

    (1)首先给大家介绍下dispatch_queue

    系统默认就有一个串行队列main_queue和并行队列global_queue:

    dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_queue_t mainQ = dispatch_get_main_queue();

    当然我们也可以手动创建dispatch_queue:

    Serial Dispatch Queue -- 线程池只提供一个子线程用来执行任务,所以后一个任务必须等到前一个任务执行结束才能开始。

    验证:

    dispatch_queue_t serial = dispatch_queue_create("demo", DISPATCH_QUEUE_SERIAL);

    dispatch_async(serial, ^{

    sleep(5);

    NSLog(@"1 queue=%@",[NSThread currentThread]);

    });

    dispatch_async(serial, ^{

    sleep(3);

    NSLog(@"2 queue=%@",[NSThread currentThread]);

    });

    dispatch_async(serial, ^{

    sleep(1);

    NSLog(@"3 queue=%@",[NSThread currentThread]);

    });

    打印结果:

    15:51:04.004 TestReplaykit[8802:223039] 1 queue={number = 2, name = (null)}

    15:51:07.010 TestReplaykit[8802:223039] 2 queue={number = 2, name = (null)}

    15:51:08.015 TestReplaykit[8802:223039] 3 queue={number = 2, name = (null)}

    Concurrent Dispatch Queue -- 线程池提供多个线程来执行任务,所以可以按序启动多个任务并发执行。

    验证:

    dispatch_queue_t serial = dispatch_queue_create("demo", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(serial, ^{

    sleep(5);

    NSLog(@"1 queue=%@",[NSThread currentThread]);

    });

    dispatch_async(serial, ^{

    sleep(3);

    NSLog(@"2 queue=%@",[NSThread currentThread]);

    });

    dispatch_async(serial, ^{

    sleep(1);

    NSLog(@"3 queue=%@",[NSThread currentThread]);

    });

    15:48:14.605 TestReplaykit[8746:221997] 3 queue={number = 2, name = (null)}

    15:48:16.608 TestReplaykit[8746:221999] 2 queue={number = 3, name = (null)}

    15:48:18.607 TestReplaykit[8746:221995] 1 queue={number = 4, name = (null)}

    (2)global_queue和Main queue的简单使用:

    在开发中我们之所以用到多线程,是因为很多耗时操作会阻塞主线程,造成页面假死,为了更好的用户体验我们会把这些耗时操作放到global_queue中来完成,完成后我们必须回到主线程中来刷新UI,所以在开发中凡是涉及到UI的逻辑我们都要把代码放到main_queue中来处理

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

             NSURL * url = [NSURL URLWithString:@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"];

             NSData * data = [[NSData alloc]initWithContentsOfURL:url];

             UIImage *image = [[UIImage alloc]initWithData:data];

            if (data != nil) {

                        dispatch_async(dispatch_get_main_queue(), ^{

                       //回到主线程刷新UI

                       self.imageView.image = image;

                  });

            }

    });

    (3)dispatch_group_async的使用

    dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很常用,比如你执行多个下载任务,当任务都下载完成后你才通知界面说完成的了。下面是一段例子代码:

    dispatch_queue_t serial = dispatch_get_global_queue(0, 0);

    dispatch_group_t group = dispatch_group_create();

    dispatch_group_async(group,serial, ^{

    sleep(5);

    NSLog(@"1 queue=%@",[NSThread currentThread]);

    });

    dispatch_group_async(group,serial, ^{

    sleep(3);

    NSLog(@"2 queue=%@",[NSThread currentThread]);

    });

    dispatch_group_async(group,serial, ^{

    sleep(1);

    NSLog(@"3 queue=%@",[NSThread currentThread]);

    });

    dispatch_group_notify(group, serial, ^{

    NSLog(@"4 queue=%@",[NSThread currentThread]);

    });

    15:59:51.063 TestReplaykit[8978:225207] 3 queue={number = 2, name = (null)}

    15:59:53.062 TestReplaykit[8978:225208] 2 queue={number = 3, name = (null)

     15:59:55.062 TestReplaykit[8978:225206] 1 queue={number = 4, name = (null)}

     15:59:55.063 TestReplaykit[8978:225206] 4 queue={number = 4, name = (null)}

    (4)栈栏函数dispatch_barrier_async

    dispatch_queue_t serial = dispatch_get_global_queue(0, 0);

    dispatch_async(serial, ^{

    sleep(5);

    NSLog(@"1 queue=%@",[NSThread currentThread]);

    });

    dispatch_async(serial, ^{

    sleep(3);

    NSLog(@"2 queue=%@",[NSThread currentThread]);

    });

    dispatch_barrier_async(serial, ^{

    sleep(1);

    NSLog(@"4 queue=%@",[NSThread currentThread]);

    });

    dispatch_async(serial, ^{

    sleep(1);

    NSLog(@"3 queue=%@",[NSThread currentThread]);

    });

    运行结果:

    16:40:24.026 TestReplaykit[9775:234799] 4 queue={number = 2, name = (null)}

     16:40:24.026 TestReplaykit[9775:234802] 3 queue={number = 3, name = (null)}

    16:40:26.025 TestReplaykit[9775:234798] 2 queue={number = 4, name = (null)}

    16:40:28.025 TestReplaykit[9775:234800] 1 queue={number = 5, name = (null)}

    如果使用dispatch_queue_t serial = dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL);

    根据FIFO原则肯定为顺序执行,感兴趣的同学可以自己验证下。

    如果使用dispatch_queue_t serial = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);

    dispatch_queue_t serial = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(serial, ^{

    sleep(5);

    NSLog(@"1 queue=%@",[NSThread currentThread]);

    });

    dispatch_async(serial, ^{

    sleep(3);

    NSLog(@"2 queue=%@",[NSThread currentThread]);

    });

    dispatch_barrier_async(serial, ^{

    sleep(1);

    NSLog(@"4 queue=%@",[NSThread currentThread]);

    });

    dispatch_async(serial, ^{

    sleep(1);

    NSLog(@"3 queue=%@",[NSThread currentThread]);

    });

    16:45:07.788 TestReplaykit[9878:236344] 2 queue={number = 2, name = (null)}

    16:45:09.788 TestReplaykit[9878:236347] 1 queue={number = 3, name = (null)}

    16:45:10.792 TestReplaykit[9878:236344] 4 queue={number = 2, name = (null)}

    16:45:11.795 TestReplaykit[9878:236347] 3 queue={number = 3, name = (null)}

    由上我们可以得出一个结论,我们可以通过栈栏函数来控制子线程执行顺序,但是queue不能是系统的global_queue,只能是自己创建的queue_create类型,同时参数只能是DISPATCH_QUEUE_CONCURRENT

    (5)dispatch_once

    用处也很多,它会让我们的某个操作在生命周期中只执行一次,常用在单例模式中

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        // 执行一次

    });

    (6)dispatch_after

    如果我们需要做一些延时操作是可以通过dispatch_after来完成

    float delaySecond = 2.0;

     dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

            // code to be executed on the main queue after delay

    });

    (7)GCD定时器,不受runloop影响,虽然稍微复杂点,但是效率还是很高的

    // 获得队列

    dispatch_queue_t queue = dispatch_get_main_queue();

    // 创建一个定时器,这里的定时器(dispatch_source_t类型)其实是个OC对象,所以必须强引用

    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

    // 设置定时器的各种属性

    // GCD的时间参数,一般是纳秒,NSEC_PER_SEC=10的9次方纳秒

    //何时开始执行第一个任务,比当前时间晚1秒,

    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));

    uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);//每隔多长时间执行一次

    dispatch_source_set_timer(self.timer, start, interval, 0);

    // 设置回调

    dispatch_source_set_event_handler(self.timer, ^{

    NSLog(@"------------%@", [NSThread currentThread]);

    count++;

    // 取消定时器

    dispatch_cancel(self.timer);

    self.timer = nil;

    });

    // 启动定时器

    dispatch_resume(self.timer);

    小结:

    dispatch_async+dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL);开启一条子线程

    dispatch_async+dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);系统决定

    dispatch_async+dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);系统决定

    dispatch_async+dispatch_get_main_queue();不会开启子线程

    dispatch_sync+dispatch_get_main_queue();线程死锁

    dispatch_sync+其他;都不会开启子线程

    如果线程之间有依赖关系,可以通过栈栏函数或者dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL)来完成。

    如果需要监听子线程,可以通过dispatch_group_t来完成。

    如有不足的地方请大家指正。

    相关文章

      网友评论

        本文标题:OC中GCD使用

        本文链接:https://www.haomeiwen.com/subject/eqruwttx.html