网络相关

OC中GCD使用

2017-02-21  本文已影响61人  CodeFXQ

一、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来完成。

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

上一篇下一篇

猜你喜欢

热点阅读