31

iOS开发系列-多线程之GCD扫盲篇

 5 years ago
source link: http://www.cocoachina.com/ios/20180927/25045.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

iOS开发系列-多线程之GCD扫盲篇

user.png 游星啊· 2018-09-27 阅读数 2900
本文来自 VinlorJiang ,作者 游星啊

1.概念相关

1.1 GCD(Grand Central Dispatch)是基于C语言开发的多线程开发技术(其它多线程技术后面再续),完全面向过程,使用起来也是最简单的,主要是适应当今多核并行运算需求下提出的一套解决方案。比如说在智能手机刚问世的那个年代,打开手机QQ,当此时有了其它应用需要被启动的时候,此时必须把QQ关掉才能打开你想要启动的应用。所以在这种需求背景下,伟大的GCD技术应运而生;

1.2 队列,遵循先进先出原则(FIFO),GCD提供的队列有:串行队列、并行队列、主队列、全局队列;

1.3 队列特点:

串行队列SerialQueue:只有一个线程,按加入队列先后顺序执行;

并行队列ConcurrentQueue:有多个线程,同时执行,不等待;

主队列MainQueue:  一定在线程中执行,

全局队列GlobalQueue:有多个线程,同时执行,不等待;

1.4 任务执行方式与特征:

  • 同步执行Sync:当前线程+等待上一个任务完成再执行当前任务。

  • 异步执行Async:子线程+同时执行每一个子线程的任务。

2.队列任务组合方式

2.1 组合方式:

  • 并行队列 + 异步执行 = 多个任务同时执行 + 子线程执行;

  • 串行队列 + 异步执行 = 多个任务顺序执行 + 子线程执行;

  • 全局队列 + 异步执行 -> 子线程执行+同时执行

  • 主队列 + 异步执行 -> 回到主线程方式二

  • 串行队列 + 同步执行(很少使用) = 任务顺序执行 + 主线程执行

  • 并行队列 + 同步执行(不用该组合) = 任务顺序执行 + 主线程执行

  • 主队列 + 同步执行     -> 死锁(两个任务处于相互等待)

2.2 demo演示:

并行队列 + 异步执行:

webp

webp

串行队列 + 异步执行:

webp

webp

全局队列 + 异步执行:

webp

webp

主队列 + 异步执行:

webp

webp

串行队列 + 同步执行:

webp

webp

并行队列 + 同步执行:

webp

webp

主队列 + 同步执行:

webp

webp

3.其它API

3.1 dispatch_set_target_queue: 我们都知道队列都是有优先级的,当不设置优先级的时候,系统都采用了默认优先级的线程,当如果在某处想修改优先级的话就需要用到 dispatch_set_target_queue,使用方法如下:

dispatch_queue_t mySerialQueue = dispatch_queue_create("com.helloWorld", NULL);
dispatch_queue_t myGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue(mySerialQueue, myGlobalQueue);// 第一个参数为需要修改优先级的队列,第二个为需要修改成什么样的队列
// 注意:第一个参数不可使用全局队列与主队列
复制代码

3.2 dispatch_after : 顾名思义就是指定时间后执行某个任务,说白了就是睡一段时间,使用方法如下:

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{

NSLog(@"hello world");
    });
复制代码

3.3 dispatch_group :在该队列中多个任务完成后需要执行某个任务时,可以用group

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, ^{
        NSLog(@"0");
    });
dispatch_group_async(group, queue, ^{
        NSLog(@"1");
    });
dispatch_group_async(group, queue, ^{
        NSLog(@"2");
    });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"game over");
    });
执行结果如下:
0
1
2
game over
复制代码

3.4 dispatch_barrier : 主要用来处理“数据竞争”问题,当有多个线程同时需要读写某个数据的时候,为了防止获取到的数据是一个旧数据,可用此解决。如果在下面代码,当想在reading2后修改某个数据,此时正确姿势应该使用dispatch_barrier来保证

reading2后面读取到的是最新数据:

dispatch_queue_t myConcurrentQueue = dispatch_queue_create("com.barrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"reading0");
});
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"reading1");
});
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"reading2");
});
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"reading3");
});

// 为保证读写的正确性,应在需要修改的代码插入dispatch_barrier,如下:
dispatch_queue_t myConcurrentQueue = dispatch_queue_create("com.barrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"reading0");
});
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"reading1");
});
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"reading2");
});
dispatch_barrier_async(myConcurrentQueue, ^{
        NSLog(@"writing");
    });
dispatch_async(myConcurrentQueue, ^{
    NSLog(@"reading3");
});
复制代码

作者:VinlorJiang
链接:https://www.jianshu.com/p/e2fe4f9b7c39


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK