这是我参与「第四届青训营 -iOS场」笔记创作活动的的第3篇笔记

多线程

知识

进程与线程

  • 进程是资源分配的最小单位,独立运转在专用并受维护的内存空间中,能够包含多个线程
  • 线程是操作系统调用的最小单位,私有寄存器、栈、线程局部存储(TLS),同享进程的内存空间(代码段、数据段、堆、文件资源等)

串行、并行、并发

  • 串行,一个个履行
  • 并行,多个使命在同一时刻被履行
  • 并发,同一时刻段,多个使命切换履行
  • 多线程编程,让多个CPU并发处理多个线程的指令

线程生命周期

  • 新建、就绪、运转、堵塞、死亡
  • 单个CPU上运转多个线程,会让每个线程轮番履行一小段时刻片,进行不断切换履行

iOS中多线程

  • POSIX Thread,类Unix系统通用的多线程API,跨渠道/可移植,C语言接口
  • NSThread,OC接口,通过KVO监听特点,需要手动管理生命周期
  • gcd,Grand Central Dispatch,开发者只需要重视使命编写,自动使用更多CPU内核,管理线程生命周期
  • NSOperation,OC接口,面向对象,支持设定使命并发数,用于KVO监听使命状态,设定使命的依赖关系

runloop

  • 事件承受和分发机制的完结,让线程在恰当的时刻处理使命不会退出
  • runloop,在程序运转时就会启动
  • 主线程又称UI线程,主要用于描绘UI和UI交互,耗时操作应当放在子线程中

gcd

  • 使命,用block方式提交使命
  • 行列,使命派发行列,先进先出,追加的方式加入到行列
  1. 串行行列,使命次序履行,使命完毕后,才干履行下一个使命,单个线程依次履行
  2. 并发行列,异步履行,分发到多个线程履行

行列获取

  • Dispatch Queue,提供主行列(串行行列,使命都会被派发到主线程)、global(并发行列,界说四种不同优先级的行列)
  • 自界说行列
// DISPATCH_QUEUE_SERIAL 串行行列
// DISPATCH_QUEUE_CONCURRENT 并行行列
dispatch_queue_create("queue.name", DISPATCH_QUEUE_CONCURRENT);

履行

  • 同步履行,等候履行完,才干持续履行,不具备敞开线程的能力
  1. 并发行列,不敞开新线程,在当时线程串行履行
  2. 串行行列(非主行列),不敞开新线程,在当时线程串行履行
  3. 主行列(主行列使命只会交给主线程履行),产生死锁,dispatch_sync等候主行列中使命被主线程履行,主线程等候dispatch_sync被履行完结
dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
  • 异步履行,提交到履行行列,不等候履行行列,能够在新的线程履行使命,具备敞开线程的能力
  1. 并发行列,敞开新线程,在新线程并发履行
  2. 串行行列(非主行列),敞开新线程,在新线程串行履行
  3. 主线程,不敞开新线程,在当时线程的下一个runloop履行使命
dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)

多线程辅助工具

延时履行

  • dispatch_after,3秒后往主行列追加到派发行列,履行时刻不确定
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_MSEC);
// 延时时刻,履行的行列,履行block
dispatch_after(time, dispatch_get_main_queue(), ^{
    //
});

组履行

  • 相当于CountDownLatch,悉数履行完结后再履行notify的动作,异步履行
dispatch_group_t group = dispatch_group_create();
// 优先级,保留参数
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, block0);
dispatch_group_async(group, queue, block1);
dispatch_group_async(group, queue, block2);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
// block...
dispatch_group_notify(group, mainQueue, ^{
    // 使命悉数履行完结后,履行当时notify中的使命,追加到主行列中
});
  • 超时等候,堵塞当时线程等候
dispatch_group_t group = dispatch_group_create();
// 优先级,保留参数
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, block0);
dispatch_group_async(group, queue, block1);
dispatch_group_async(group, queue, block2);
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_MSEC);
// 最多等候group使命履行多久time,根据回来值不同判断是履行完结回来,仍是超时回来
// 0 履行完结,非0超时
long result = dispatch_group_wait(group, time);
// 其他处理...

快速迭代

  • dispatch_apply,依照履行的次数将指定使命追加到行列中,假如传入串行行列,会次序遍历,性能不如普通遍历
  • 同步履行接口,会堵塞当时线程,直到使命履行完才会持续履行
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(3, queue, ^(size_t iteration) {
    NSLog(@"%ld", iteration);
});
  • 防止直接在主线程履行,堵塞主线程运转
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
    // 子线程遍历
    dispatch_apply(3, globalQueue, ^(size_t iteration) {
        //..
    });
    dispatch_async(mainQueue, ^{
        //.. 回到主线程履行
    });
});

once

  • 某些操作在整个声明周期只履行一次
  • 单例形式
+ (instancetype)sharedInstance {
    // 只会被初始化一次
    static CommonSortUtil *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (!instance) {
            instance = [[CommonSortUtil alloc] init];
        }
    });
    return instance;
}

线程安全

  • 原子操作,单个机器指令履行的操作,高级语言写出一条句子,往往是多条机器指令
  • 临界区,不能被并发履行的一段代码(同享数据、代码块)
  • 线程同步,一个线程拜访临界区的时分,其他线程不能对临界区进行拜访,对临界区的拜访变成原子性的
  • 锁,互斥量是最简单的锁,锁被一个线程占用时,其他线程尝试获取时只能进行等候,直到被开释;获取锁的线程能够进行重用,还有递归锁、读写锁、条件变量、信号量

死锁,多个线程履行过程中,由于竞赛资源导致互相堵塞等候;不可抢占有、彼此等候、等候且占有、循环等候

iOS多线程和GCD | 青训营笔记

atomic和nonatomic

  • @property的特点
  • atomic,默认,对特点getter/setter调用是线程安全的,需要耗费资源为特点加锁
  • nonatomic,拜访不是线程安全的,拜访功率比atomic高

栅门

  • 完结读写锁,读读同享,读写互斥
  • dispatch_barrier_async等候前面的使命履行完毕,才会持续履行后边的使命
// 大局行列完结不了
// dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t globalQueue = dispatch_queue_create("queue.init", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(globalQueue, ^{
    NSLog(@"read1...");
});
dispatch_async(globalQueue, ^{
    NSLog(@"read2...");
});
dispatch_async(globalQueue, ^{
    NSLog(@"read3...");
});
// 写操作
dispatch_barrier_async(globalQueue, ^{
    for (int i = 0; i <= 50000000; i++) {}
    NSLog(@"write1...");
});
dispatch_async(globalQueue, ^{
    NSLog(@"read4...");
});
dispatch_async(globalQueue, ^{
    NSLog(@"read5...");
});
dispatch_barrier_async(globalQueue, ^{
    NSLog(@"write2...");
});
dispatch_async(globalQueue, ^{
    NSLog(@"read6...");
});
NSLog(@"end...");
[NSThread sleepForTimeInterval:100000];

信号量

  • 信号量大于0,能够减一;信号量等于0,堵塞等候

初始值为1,能够完结互斥锁的效果

  • 创立
dispatch_semaphore_create(3)
  • 等候,永久等候semaphore,为0时堵塞等候
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
  • 交还信号量
dispatch_semaphore_signal(semaphore)

使用场景

  • 从网络加载图片
  • dispatch_group_t完结悉数恳求回来后,再进行改写使命
  • 线程安全容器类
  1. 读写锁
  2. 移除时,用栅门做隔离
- (id)getObject {
    id lastObject = nil;
    // 堵塞获取元素,使命派发到concurrentQueue,但是串行履行
    dispatch_sync(self.concurrentQueue, ^{
        lastObject = [self.array lastObject];
    });
    return lastObject;
}
- (void)removeLastObject {
    // 等候行列中前面的使命被履行完毕,再进行履行
    dispatch_barrier_async(self.concurrentQueue, ^{
        [self.array removeLastObject];
    });
}

常见问题

  • 死锁
  1. 递归获取非递归锁
  2. 两个线程互相等候
  • 非主线程操作UI
  • 线程不安全的容器读写崩溃,数组越界/野指针拜访