iOS

iOS多线程总结

iOS,多线程

Posted by Karim on February 16, 2019

iOS多线程总结

碎碎念

这是2019的第一篇,虽然原来这篇大概在上年7、8月的时候就开始写了,但是因为各种生活+工作的事情,再加上对文章保持严谨的态度,在写之前写过很多测试代码,同时也顺便自己复习了一下,所以一直搁置到现在才写完。

前言

最初写这一篇,是曾经被抛出过一个很泛的问题,“说说你知道的iOS多线程的知识点?”,对于一个iOS开发者来说,多线程绝对不陌生,而且经常会用到,但是遇到这个问题的时候,很容易想到什么就说什么,给别人一种“不熟悉”的感觉,所以借着这个原因,我也去梳理了一下关于多线程的知识,写下这篇文章,作为记录。

如果没有耐心看完,可以直接看这里

正文

聊到多线程,肯定少不了要说说进程和线程之间的关系。

什么是进程?

在操作系统中,开启一个任务会开启一个进程。
在iOS上,一个app对应一个进程。每个进程运行在其专用且受保护的内存空间中。
例如在mac上打开终端,然后再输入ps就会看到开启了一条bash

MacdeMacBook-Pro:~ Fidetro$ ps
  PID TTY           TIME CMD
 8329 ttys000    0:00.03 -bash

如果再开打开一个终端,再次输入ps就会看到开启了两个bash

MacdeMacBook-Pro:~ Karim$ ps
  PID TTY           TIME CMD
 8329 ttys000    0:00.03 -bash
 8651 ttys001    0:00.02 -bash

一个进程中又至少会开启一条线程,在iOS中,我们把那条线程叫做主线程。一个进程可以开启多条线程。

什么是线程?

线程是指给代码单独执行的通道。 —-翻译自苹果文档

iOS中提供了4个方案让我们使用多线程,分别是:

  • Pthreads
  • NSThread
  • GCD
  • NSOperation

这篇主要是讲GCD,其他方案的资料,可以参考总结的图。

GCD

GCD是一套C语言编写的接口,其代码也是开源,它为我们提供多线程技术,GCD中最核心的两个概念,任务和队列。

在iOS中使用GCD创建队列,调用dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
第一个参数代表队列的名字,第二参数代表队列的类型,使用DISPATCH_QUEUE_SERIAL创建串行队列,使用DISPATCH_QUEUE_CONCURRENT创建并行队列。

串行队列


串行队列,任务按照进入顺序执行。遵循FIFO(先进先出)原则。

并行队列


并行队列,任务会并行执行,无法保证任务完成的时机是添加的顺序。

异步

异步执行不会等待内部代码全部执行完然后返回,而是直接往下执行。

同步

同步执行会等待内部代码执行完毕之后,再往下执行。

  同步 异步
串行队列 不会创建新的线程,而是在当前线程执行。任务按顺序执行,等待内部代码执行完毕后往下执行。 调用dispatch_queue_create()会创建新的线程,任务按顺序执行,不会等待内部代码全部执行完然后返回,而是直接往下执行。
并行队列 与串行同步一致。 调用dispatch_async()会创建新的线程,任务在不同的线程下执行。

dispatch_after

dispatch_after()是iOS最常用的延时方法。

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //两秒后执行
    });

dispatch_group_t

dispatch_group_t用来监听多个异步任务的完成。
GCD提供了两种方式去获取dispatch_group_t完成的回调。
第一种是dispatch_group_notify(),不会阻塞当前线程,在所有任务完成后,回调到block;
第二种是dispatch_group_wait(),会阻塞当前线程,等待所有任务完成后才往下走,dispatch_group_wait()不可以在主线程调用,否则会造成死锁。

dispatch_group_notify()的例子如下:

   dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_async(group, queue, ^(){
        dispatch_group_async(group, dispatch_get_main_queue(), ^(){
            dispatch_group_enter(group);
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"1");
                dispatch_group_leave(group);
            });
        });
    });
    dispatch_group_async(group, queue, ^(){
        dispatch_group_async(group, dispatch_get_main_queue(), ^(){
            dispatch_group_enter(group);
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"2");
                dispatch_group_leave(group);
            });
        });
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^(){
        NSLog(@"3");
    });

输出结果:

2018-12-19 15:12:27.770146+0800 GCD[70391:2895561] 2
2018-12-19 15:12:29.220584+0800 GCD[70391:2895561] 1
2018-12-19 15:12:29.220987+0800 GCD[70391:2895561] 3

dispatch_group_wait()例子如下:

dispatch_queue_t asyncQueue = dispatch_queue_create("com.group.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(asyncQueue, ^{
        dispatch_group_t group = dispatch_group_create();
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        dispatch_group_async(group, queue, ^(){
            dispatch_group_async(group, dispatch_get_main_queue(), ^(){
                dispatch_group_enter(group);
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    NSLog(@"1");
                    dispatch_group_leave(group);
                });
            });
        });
        dispatch_group_async(group, queue, ^(){
            dispatch_group_async(group, dispatch_get_main_queue(), ^(){
                dispatch_group_enter(group);
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    NSLog(@"2");
                    dispatch_group_leave(group);
                });
            });
        });
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        NSLog(@"3");
    });

输出结果:

2019-02-16 12:21:12.300569+0800 GCD[79234:3427553] 2
2019-02-16 12:21:13.751251+0800 GCD[79234:3427553] 1
2019-02-16 12:21:13.751419+0800 GCD[79234:3427633] 3

dispatch_barrier_async

dispatch_barrier相当于一个“栅栏”的作用,在相同的队列中,会先执行在dispatch_barrier之前的任务,然后dispatch_barrier之后的任务,都需要等待dispatch_barrier完成之后,才会执行block里面的内容。

另外苹果文档中有写道:

The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_sync function.

使用dispatch_barrier_async的时候,需要自定义队列才有效,如果使用全局队列或者同步队列,效果和dispatch_sync()同步函数一样。

    //防止文件读写冲突,可以创建一个串行队列,操作都在这个队列中进行,没有更新数据读用并行,写用串行。
    dispatch_queue_t dataQueue = dispatch_queue_create("com.karim.gcd.dataqueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(dataQueue, ^{
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"read data 1");
    });
    dispatch_async(dataQueue, ^{
        NSLog(@"read data 2");
    });
    //等待前面的都完成,在执行barrier后面的
    dispatch_barrier_async(dataQueue, ^{
        NSLog(@"write data 1");
        [NSThread sleepForTimeInterval:1];
    });
    dispatch_async(dataQueue, ^{
        [NSThread sleepForTimeInterval:1.f];
        NSLog(@"read data 3");
    });
    dispatch_async(dataQueue, ^{
        NSLog(@"read data 4");
    });

输出结果如下:

2018-12-19 15:26:50.297477+0800 GCD[70716:2943544] read data 2
2018-12-19 15:26:52.302570+0800 GCD[70716:2943541] read data 1
2018-12-19 15:26:52.302925+0800 GCD[70716:2943541] write data 1
2018-12-19 15:26:53.306632+0800 GCD[70716:2943544] read data 4
2018-12-19 15:26:54.307808+0800 GCD[70716:2943541] read data 3

总结

我把一些关于多线程的知识点整理在这个图里了,如果有遗漏,可以联系我,我会即时补充上去。

参考

iOS并发编程

ios的线程和同步异步操作


请保持转载后文章内容的完整,以及文章出处。本人保留所有版权相关权利。

分享到: