iOS中多线程GCD(Grand Center Dispatch)

ios中多线程GCD(Grand Center Dispatch)

  1. 特别注意ios中主线程又称作为 UI线程, 主要任务就是处理UI事件, 即显示和刷新UI,
  2. 只有主线程具有直接修改UI的能力, 那些耗时的(从网络获取数据 | 加载图片 | 数据库读取 | IO等)操作要放在子线程(又称为后台线程或者异步线程)中处理, 这样可以提高程序执行效率和资源利用率, 最重要的用户的 UI 的体验也会很好.
  3. 死锁: 两个或者多个线程都要等待对方完成某个操作才能进行下一步, 从而产生死锁. 如: 在主线程串行队列执行同步任务,会产生死锁.

常见代码示例:

dispatch_queue_t globalQueue = dispatch_get_global_queu(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(globalQueue, ^{
    //异步 加载数据
    NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];
    NSError *error;
    NSString *htmlData = [NSString stringWithContentsWithURL:url encoding:NSUTF8StringEncoding error:&error];
    //加载完毕切换到 主线程中更新UI
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"回到主队列更新UI界面 = %@", htmlData);
    });
});

同步和异步

同步和异步决定了要不要开启新的线程

  • 同步: 在当前线程中执行任务, 不具备开启新线程的能力.

    dispatch_sync(queue, ^{});
    
  • 异步: 在新的线程中执行任务, 具备开启新线程的能力.

    dispatch_async(queue, ^{});
    

并发和串行

并发和串行决定了任务的执行方式

  • 并发: 多个任务并发(同时)执行.

    并发队列: dispatch_get_global_queue(0, 0);
    
  • 串行: 一个任务执行完毕后, 再执行下一个任务.

    主队列: dispatch_get_main_queue(); 
    

同步和异步 & 并发和串行

ios中常用的搭配组合:

  • 同步--串行(详情看下面自定义串行队列例子), 有特殊情况(主队列要对应异步, 否则会造成死锁)

    dispatch_async(dispatch_get_main_queue(), ^{});
    
  • 异步--并行:

    dispatch_async(dispatch_get_global_queue(0, 0), ^{});
    
  • 其他情况等同于上面两种情况, 就不多介绍了

GCD中三种队列类型

  • 主线程串行队列(main queue)
  • 全局并发队列(global queue)
  • 自定义队列(custom queue)
  • 还有一个就是基于前面的三种队列形成的队列组(group queue)

详细介绍

GCD编程核心:就是dispatch队列.

即: 将任务放到block中, dispatch分发到相对应的队列中去执行.

  1. 主线程队列(main queue)--> 串行:

    即将任务(block)放到主队列中去, 在主线程中执行, 注意主队列默认是串行的(即:若此刻主队列正在执行任务, 那么刚放进行来的block任务就要等待前面的任务block执行完, 才能执行哦).

    ios中获取主队列: dispatch_get_main_queue()
    
    主队列中执行同步任务会造成死锁哦:
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"会造成死锁");
    });
    
  2. 全局并发队列(global queue)--> 并发:

    全局并发队列由所有进程共享, 分为(高, 中(默认为中), 低, 后台)四个优先级.

    //程序默认的队列级别,一般不要修改:
    DISPATCH_QUEUE_PRIORITY_DEFAULT == 0
    //HIGH
    DISPATCH_QUEUE_PRIORITY_HIGH
    //LOW
    DISPATCH_QUEUE_PRIORITY_LOW
    //BACKGROUND
    DISPATCH_QUEUE_PRIORITY_BACKGROUND
    
    ios中获取全局并发队列:dispatch_get_global_queue(0, 0)
    
    全局并发队列执行同步任务会导致页面卡顿:
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"会造成页面卡顿");
    });
    
    全局并发队列执行多个任务时, 执行的顺序是不确定的.
    因为全局并发队列是由系统默认生成的, 故也无法来控制执行的继续和中断.
        
    
  3. 自定义队列:--> 串行或者并发:

    ios中创建队列: dispatch_queue_create()
    //串行队列创建
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue_name", DISPATCH_QUEUE_SERIAL);
    
    //自定义串行队列 同步执行多个任务
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    

NSLog(@"current task");
dispatch_sync(serialQueue, ^{
NSLog(@"最先加入自定义串行队列");
sleep(2);
});
dispatch_sync(serialQueue, ^{
NSLog(@"次加入自定义串行队列");
});
NSLog(@"next task");

//自定义串行队列嵌套在执行同步任务会产生死锁:
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);

dispatch_sync(serialQueue, ^{
//该代码段后面的代码都不会执行,程序被锁定在这里
NSLog(@"会执行的代码");
dispatch_sync(serialQueue, ^{
NSLog(@"代码不执行");
});
});

注意: 不要嵌套使用 同步 串行 队列执行任务

//自定义创建并发队列:
dispatch_queue_t conCurrentQueue = dispatch_queue_create("conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);

//并发队列执行同步是没有意义的(等同于异步), 这里就不多介绍了. 一般情况下, 使用系统默认的全局并发队列就已经足够了, 
//推荐使用系统默认的全局并发队列.
```
  1. 队列组(group queue):

    将多个线程进行分组, 最大的好处就是可以获知所有进程的完成情况.
    使用场景: 比如说 同时下载多张图片时, 有这么一个需求:要等所有的图片下载完毕后, 才能去更新UI(回到主线程或者去执行其他操作), 这时候就要用到队列组了.

```
ios获取队列组: dispatch_group_create()
通过dispatch_group_notify 可以对队列组中的所有线程进行监听进程完成情况.

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");
```

GCD中一些系统提供的(常用)dispatch方法

  1. 延时方法:

    dispatch_after(time, queue, ^{});
    
    dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_after(delayTime, mainQueue, ^{
        NSLog(@"3秒后添加到主线程中执行...");
    });
    
  2. 多次执行某一任务:

    dispatch_apply(count, queue, ^(size_t index));
    
    为了不阻塞主线程, 一般dispatch_apply放在异步并行队列中执行,
    执行完了切到主队列中再次执行
    
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    

NSLog(@"current task");
dispatch_async(globalQueue, ^{
dispatch_queue_t applyQueue = dispatch_get_global_queue(0, 0);
//第一个参数,3--block执行的次数
//第二个参数,applyQueue--block任务提交到的队列
//第三个参数,block--需要重复执行的任务
dispatch_apply(3, applyQueue, ^(size_t index) {
NSLog(@"current index %@",@(index));
NSThread.sleep(1);
});
NSLog(@"dispatch_apply 执行完成");
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
NSLog(@"回到主线程更新UI");
});
});
NSLog(@"next task");

//注意: 嵌套使用dispatch_apply会导致死锁。
```
  1. 只执行一次的代码:

    dispatch_once保证在app运行期间, block中的代码只执行一次
    ios最常用就是 单例模式
    
    如Person类单例创建方法:
    
    static Person *person = nil;
    + (instanceType)sharedManager {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            person = [self alloc] init];
        });
        return person;
    }
    
  2. 栅栏函数: 类似队列组的作用.

    dispatch_barrier_async(queue, ^{});
    
    用法:  在并行队列中, 等待在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");
});
```

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

推荐阅读更多精彩内容