这是我参与「第四届青训营 -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方式提交使命
- 行列,使命派发行列,先进先出,追加的方式加入到行列
- 串行行列,使命次序履行,使命完毕后,才干履行下一个使命,单个线程依次履行
- 并发行列,异步履行,分发到多个线程履行
行列获取
- Dispatch Queue,提供主行列(串行行列,使命都会被派发到主线程)、global(并发行列,界说四种不同优先级的行列)
- 自界说行列
// DISPATCH_QUEUE_SERIAL 串行行列
// DISPATCH_QUEUE_CONCURRENT 并行行列
dispatch_queue_create("queue.name", DISPATCH_QUEUE_CONCURRENT);
履行
- 同步履行,等候履行完,才干持续履行,不具备敞开线程的能力
- 并发行列,不敞开新线程,在当时线程串行履行
- 串行行列(非主行列),不敞开新线程,在当时线程串行履行
- 主行列(主行列使命只会交给主线程履行),产生死锁,
dispatch_sync
等候主行列中使命被主线程履行,主线程等候dispatch_sync
被履行完结
dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
- 异步履行,提交到履行行列,不等候履行行列,能够在新的线程履行使命,具备敞开线程的能力
- 并发行列,敞开新线程,在新线程并发履行
- 串行行列(非主行列),敞开新线程,在新线程串行履行
- 主线程,不敞开新线程,在当时线程的下一个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;
}
线程安全
- 原子操作,单个机器指令履行的操作,高级语言写出一条句子,往往是多条机器指令
- 临界区,不能被并发履行的一段代码(同享数据、代码块)
- 线程同步,一个线程拜访临界区的时分,其他线程不能对临界区进行拜访,对临界区的拜访变成原子性的
- 锁,互斥量是最简单的锁,锁被一个线程占用时,其他线程尝试获取时只能进行等候,直到被开释;获取锁的线程能够进行重用,还有递归锁、读写锁、条件变量、信号量
死锁,多个线程履行过程中,由于竞赛资源导致互相堵塞等候;不可抢占有、彼此等候、等候且占有、循环等候
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
完结悉数恳求回来后,再进行改写使命 - 线程安全容器类
- 读写锁
- 移除时,用栅门做隔离
- (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];
});
}
常见问题
- 死锁
- 递归获取非递归锁
- 两个线程互相等候
- 非主线程操作UI
- 线程不安全的容器读写崩溃,数组越界/野指针拜访