iOS面试首页投稿(暂停使用,暂停投稿)iOS Developer

iOS多线程GCD使用及总结

2017-06-26  本文已影响166人  王技术

这篇文章对iOS的多线程技术GCD的常用方法做了总结
另一篇NSOperation在这里
本文代码

- GCD:

简介:

GCD两个核心概念:

首先介绍GCD执行任务时常用的两个函数:

 dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
ddispatch_async(dispatch_queue_t queue, dispatch_block_t block);

同步和异步的区别:
同步 : 只在当前线程执行 , 不开其他线程
异步 : 可以在新的线程中执行任务 , 有开线程的能力 (但是不一定开,要看用什么队列)

然后是两种GCD队列:

//创建并发队列:
dispatch_queue_t queue = dispatch_queue_create("name", DISPATCH_QUEUE_CONCURRENT);
//获得全局并发队列:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
// 创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)
dispatch_queue_t queue = dispatch_queue_create("name", NULL); 
//使用dispatch_get_main_queue()获得主队列
//主队列是GCD自带的一种特殊的串行队列
//放在主队列中的任务,都会放到主线程中执行(不管是同步函数,还是异步函数)
dispatch_queue_t queue = dispatch_get_main_queue();

代码实践各种队列与函数的组合:

//同步+串行
-(void)syncSerial{
    //创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("name", DISPATCH_QUEUE_SERIAL);
    //用同步函数把任务放入串行队列:
    dispatch_sync(queue, ^{
        NSLog(@"-------download1--%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"-------download2--%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"-------download3--%@",[NSThread currentThread]);
    });
}

打印结果:

2017-06-26 13:25:53.744 HXThread[1628:247794] -------download1--<NSThread: 0x60800007b100>{number = 1, name = main}
2017-06-26 13:25:53.745 HXThread[1628:247794] -------download2--<NSThread: 0x60800007b100>{number = 1, name = main}
2017-06-26 13:25:53.745 HXThread[1628:247794] -------download3--<NSThread: 0x60800007b100>{number = 1, name = main}

结论 :
同步函数是在当前线程执行,不具备开线程的能力,然后串行队列又是的任务又是串行执行,所以没有开新的线程,大家一个一个的执行

    //获得全局并发队列
    dispatch_queue_t  queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //用同步函数 把任务加入并行队列
    //用同步函数把任务放入串行队列:
    dispatch_sync(queue, ^{
        NSLog(@"-------download1--%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"-------download2--%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"-------download3--%@",[NSThread currentThread]);
    });

打印结果:

2017-06-26 13:34:57.588 HXThread[1674:254728] -------download1--<NSThread: 0x60800007fe80>{number = 1, name = main}
2017-06-26 13:34:57.591 HXThread[1674:254728] -------download2--<NSThread: 0x60800007fe80>{number = 1, name = main}
2017-06-26 13:34:57.594 HXThread[1674:254728] -------download3--<NSThread: 0x60800007fe80>{number = 1, name = main}

结论:
同步函数是在当前线程执行,不具备开线程的能力,就算你是并发队列也不好使,任务还是在当前线程,大家一个一个的来

//异步函数 + 串行队列
-(void)asyncSerial{
    //创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("name", DISPATCH_QUEUE_SERIAL);
    //用异步函数 将任务加入串行队列
    dispatch_async(queue, ^{
        NSLog(@"-------download1--%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"-------download2--%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"-------download3--%@",[NSThread currentThread]);
    });
}

打印结果:

2017-06-26 13:48:02.264 HXThread[1720:261890] -------download1--<NSThread: 0x600000066680>{number = 4, name = (null)}
2017-06-26 13:48:02.264 HXThread[1720:261890] -------download2--<NSThread: 0x600000066680>{number = 4, name = (null)}
2017-06-26 13:48:02.264 HXThread[1720:261890] -------download3--<NSThread: 0x600000066680>{number = 4, name = (null)}

结论:
异步函数具有开线程的能力,但是串行队列是挨着来,所以就开一个线程,大家在这个新线程中一个一个整

    //创建一个并发队列
    dispatch_queue_t queue = dispatch_queue_create("name", DISPATCH_QUEUE_CONCURRENT);
    //用异步函数 将任务加入并发队列
    dispatch_async(queue, ^{
        NSLog(@"-------download1--%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"-------download2--%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"-------download3--%@",[NSThread currentThread]);
    });

打印结果:

2017-06-26 13:51:57.523 HXThread[1749:264765] -------download2--<NSThread: 0x60800007ea40>{number = 6, name = (null)}
2017-06-26 13:51:57.523 HXThread[1749:264699] -------download1--<NSThread: 0x60800007e280>{number = 5, name = (null)}
2017-06-26 13:51:57.523 HXThread[1749:264766] -------download3--<NSThread: 0x600000264e00>{number = 7, name = (null)}

结论:
异步函数已经有了开线程的能力,并发队列又是又是让任务并发执行,所以开了多条线程,大家一起干

    //获取主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    //用异步函数 把任务放到主队列中
    dispatch_async(queue, ^{
        NSLog(@"-------download1--%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"-------download2--%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"-------download3--%@",[NSThread currentThread]);
    });

打印结果:

2017-06-26 13:56:52.117 HXThread[1788:268167] -------download1--<NSThread: 0x608000078d40>{number = 1, name = main}
2017-06-26 13:56:52.121 HXThread[1788:268167] -------download2--<NSThread: 0x608000078d40>{number = 1, name = main}
2017-06-26 13:56:52.121 HXThread[1788:268167] -------download3--<NSThread: 0x608000078d40>{number = 1, name = main}

结论:
虽然异步函数有开线程的能力,但是队列是主队列,谁也不好使,大家都在主线程执行,一个一个挨着来

    //获取主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    //用同步函数 将任务加入主队列
    dispatch_sync(queue, ^{
        NSLog(@"-------download1--%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"-------download2--%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"-------download3--%@",[NSThread currentThread]);
    });

没有打印结果.程序报错
结论:
当程序执行到NSLog(@"-------download1--%@",[NSThread currentThread]);这一行的时候
这一行打印任务就加入到了主队列,因为又是在主线程,所以打印任务要在主线程的当前任务结束后立马执行
当前任务就是当前方法
但是当前方法要等打印结束之后才能结束
所以打印任务等当前方法任务结束才能执行
当前方法这个任务又等打印执行结束才能结束
就卡死了..........

以上就是两种函数和两种队列的组合使用,总结一下:

线程之间的通信:

代码实现:

//子下载图片,回到主线程更新UI
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage imageWithData:data];
        //回到主线程更新UIUI
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
});

GCD其他常用函数:

static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"这里的代码在程序执行过程中,只会执行一次");
    });
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"延时2秒后执行这里的代码");
    });
//多线程快速遍历数组
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"------索引:%zu------%@",index,[NSThread currentThread]);
    });
    //全局并发队列
    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, ^{
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        // 生成图片
        self.image1 = [UIImage imageWithData:data];
    });
    dispatch_group_async(group, queue, ^{
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        // 生成图片
        self.image2 = [UIImage imageWithData:data];
    });
    //当group中的任务都执行完了以后,就会执行group中notify中的内容
    dispatch_group_notify(group, queue, ^{
        //合成图片
        UIGraphicsBeginImageContext(CGSizeMake(100, 100));
        [self.image1 drawInRect:CGRectMake(0, 0, 50, 100)];
        [self.image2 drawInRect:CGRectMake(50, 0, 50, 100)];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
    });
    //定时器在哪个队列进行
    dispatch_queue_t queue = dispatch_get_main_queue();
    //创建定时器
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    //定时器的开始时间
    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(timer, start, interval, 0);

    // 设置回调
    dispatch_source_set_event_handler(self.timer, ^{
        NSLog(@"------------%@", [NSThread currentThread]);
    });
    
    // 启动定时器
    dispatch_resume(self.timer);

值得注意的是
GCD定时器时间间隔是纳秒
1秒 = 1*10的九次方
不过GCD为我们提供了一个宏 NSEC_PER_SEC
(1.0 * NSEC_PER_SEC) 就是一秒
GCD 定时器不受 RunLoop 运行模式的影响
有关 RunLoop 和定时器的关系, 请看我这篇文章:
http://www.jianshu.com/p/5ef8f28025b9

谢谢阅读
有不合适的地方请指教
喜欢请点个赞
抱拳了!
上一篇 下一篇

猜你喜欢

热点阅读