iOS 多线程 GCD

线程vs进程

进程: 对于移动端来讲一个APP的启动就相当于手机系统开启了一个进程,每个程序只能开启一个进程,也就是说对于手机系统而言都是单进程应用。

线程: 可以理解为独立执行的代码段,一个线程同时间只能执行一个任务。

提示: iOS程序启动之后就创建了一个主线程,用于刷新程序UI,所以才有你看到的绚丽多彩的界面。

同步和异步

线程分同步sync和异步async之分

  • 同步线程:同步线程会阻塞当前线程去执行线程内的任务,执行完之后才会反回当前线程。
  • 异步线程:异步线程不会阻塞当前线程,会开启其他线程去执行线程内的任务。(推荐使用)
dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"异步执行任务,不会阻塞当前线程");
});
dispatch_sync(dispatch_get_main_queue(), ^{
  NSLog(@"同步执行任务,注意——>");//这段代码不会打印,会产生死锁,因为同步主线程会造成主队列线程相互等待。
});

队列

GCD中一个block代码块就是一个队列,队列可以是并行也可以是串行的。默认情况下,它们是串行的,也就是说,任何给定的时间内,只能有一个单独的 block 运行。队列也可以是并行的,也就是同一时间内允许多个 block 一起执行。其实队列很大程度的帮助我们更方便的使用多线程来调度我们的功能。

主线程队列

1.获取主线程串行队列

dispatch_queue_t mainQueue = dispatch_get_main_queue();//注意:禁止再次队列中同步执行任务。因为会产生死锁

2.主线程串行队列异步执行任务,在主线程运行,不会产生死锁

dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
    NSLog(@"主队列");
});

全局并发队列

耗时的操作,比如读取网络数据,IO,数据库读写等,我们会在另外一个线程中处理这些操作,然后通知主线程更新界面
1.获取全局并发队列

//全局并发队列中 GCD 为我们提供了优先级
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/*
 #define DISPATCH_QUEUE_PRIORITY_HIGH 2
 #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
 #define DISPATCH_QUEUE_PRIORITY_LOW (-2)
 #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
 */

2.多个线程任务在全局并发队列中执行

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"current task");
dispatch_async(globalQueue, ^{
    NSLog(@"最先加入全局并发队列");
});
dispatch_async(globalQueue, ^{
    NSLog(@"次加入全局并发队列");
});
NSLog(@"next task");

控制台输出:

2015-11-18 16:54:52.202 Whisper[39827:403208] current task
2015-11-18 16:54:52.203 Whisper[39827:403208] next task
2015-11-18 16:54:52.205 Whisper[39827:403309] 最先加入全局并发队列
2015-11-18 16:54:52.205 Whisper[39827:403291] 次加入全局并发队列

异步线程的执行顺序是不确定的。几乎同步开始执行
全局并发队列由系统默认生成的,所以无法调用dispatch_resume()和dispatch_suspend()来控制执行继续或中断。

自定义队列

自定义队列就是抛开系统为我们创建的全局主队列以及全局并发队列,自己创建一个队列

//自定义串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("com.custom.serialQueue", DISPATCH_QUEUE_SERIAL);
//自定义并行队列
dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.custom.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_queue_create 后面第一个参数通常用反域名字符表示,你可以作为你识别的唯一名称,方便调试
有了队列我们就可以添加任务来执行了

示例:

dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"current task");
dispatch_async(conCurrentQueue, ^{
   NSLog(@"先加入队列");
});
dispatch_async(conCurrentQueue, ^{
   NSLog(@"次加入队列");
});
NSLog(@"next task");

控制台输出如下:

2015-11-19 10:45:22.290 Whisper[1050:26445] current task
2015-11-19 10:45:22.290 Whisper[1050:26445] next task
2015-11-19 10:45:22.290 Whisper[1050:26505] 次加入队列
2015-11-19 10:45:22.290 Whisper[1050:26500] 先加入队列

队列组

当遇到需要执行多个线程并发执行,然后等多个线程都结束之后,再汇总执行结果时可以用group queue

dispatch_group_t groupQueue = dispatch_group_create();

使用示例:

dispatch_queue_t conCurrentGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_group_t groupQueue = dispatch_group_create();
NSLog(@"current task");
dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
   NSLog(@"并行任务1");
});
dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
   NSLog(@"并行任务2");
});
dispatch_group_notify(groupQueue, mainQueue, ^{
   NSLog(@"groupQueue中的任务 都执行完成,回到主线程更新UI");
});
NSLog(@"next task");

控制台输出:

2017-08-15 17:42:42.355 TestProject[21907:682737] current task
2017-08-15 17:42:42.356 TestProject[21907:682737] next task
2017-08-15 17:42:42.356 TestProject[21907:682850] 并行任务1
2017-08-15 17:42:42.356 TestProject[21907:682849] 并行任务2
2017-08-15 17:42:42.504 TestProject[21907:682737] groupQueue中的任务 都执行完成,回到主线程更新UI
//可以阻塞当前线程 起到延迟等待效果
dispatch_group_wait(groupQueue, delayTime);

GCD 系统常用的 dispatch 方法

1.dispatch_once 实现单利模式

+ (UIColor *)defaultInstance;
{
    static UIColor *color;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        color = [UIColor colorWithRed:0.380f green:0.376f blue:0.376f alpha:1.000f];
    });
    return color;
}

上面的 block 只会运行一次。并且在连续的调用中,这种检查是很高效的

2.dispatch_after被用作延迟添加

dispatch_queue_t mainQueue = dispatch_get_main_queue();
NSLog(@"current task");
dispatch_after(delayTime3, mainQueue, ^{
  NSLog(@"3秒之后添加到队列");
});
dispatch_after(delayTime2, mainQueue, ^{
   NSLog(@"2秒之后添加到队列");
});
NSLog(@"next task");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    NSLog(@"延迟两秒执行");
});

3.dispatch_apply

dispatch_apply函数的功能:把一项任务提交到队列中多次执行,队列可以是串行也可以是并行,dispatch_apply不会立刻返回,在执行完block中的任务后才会返回,是同步执行的函数。

dispatch_queue_t seriaQueue = dispatch_queue_create("com.seriaQueue", DISPATCH_QUEUE_SERIAL);
dispatch_apply(3, seriaQueue, ^(size_t index) {
    index++;
    NSLog(@"index=%zu",index);
});
NSLog(@"执行完成");

控制台输出:

//如果 seriaQueue 为并行队列 index 也可能是无须输出
2017-08-15 17:56:09.125 TestProject[22222:694749] index=1
2017-08-15 17:56:09.125 TestProject[22222:694749] index=2
2017-08-15 17:56:09.126 TestProject[22222:694749] index=3
2017-08-15 17:56:09.126 TestProject[22222:694749] 执行完成

4.dispatch_barrier_async 栅栏的作用

功能:是在并行队列中,等待在dispatch_barrier_async之前加入的队列全部执行完成之后(这些任务是并发执行的)再执行dispatch_barrier_async中的任务,dispatch_barrier_async中的任务执行完成之后,再去执行在dispatch_barrier_async之后加入到队列中的任务(这些任务是并发执行的)。

示例:

dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", 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");
});

控制台输出:

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

推荐阅读更多精彩内容