OC中GCD使用

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容