【iOS基础】多线程
基础
概念
-
进程
- 在系统中正在运行的一个应用程序
- 每个进程之间是独立的,运行在其专用的且受保护的内存空间内
- iOS系统是单进程的
-
线程
- 进程的基本执行单元,
- 处理器/CPU调度的基本单位
- 一个进程的所有任务都在线程中执行,进程想要执行任务,必须要有线程(进程至少要有一条线程),程序会默认开启一条线程(主线程/UI线程)
- 多线程原理:CPU在单位时间片里快速在各个线程之间切换
-
时间片: cpu在多个任务之间进行快速切换的时间间隔
进程和线程对比
| 对象 | 线程 | 进程 |
|---|---|---|
| 地址空间 | 同一进程下的线程共享本进程的地址空间 | 进程间地址空间独立 |
| 资源拥有 | 同一进程下的线程共享本进程的资源(内存、I/O,CPU等) | 进程间资源独立 |
| 崩溃 | 一个线程崩溃→整个进程死掉 | 一个进程崩溃,保护模式下不会对其他进程产生影响 |
| 切换 | 1. 涉及频繁切换 2. 同时进行+共享某些变量 | 晋城切换,消耗的资源大,效率低 |
| 执行过程 | 不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制 | 每个独立进程有一个程序运行的入口和顺序执行序列 |
意义
优点:
-
适当提高程序的执行效率
-
适当提高资源利用率(CPU、内存…)
-
线程上的任务执行完成后,线程会自动销毁
缺点:
-
开启线程耗时(约4ms)
-
占用一定的内存空间(iOS中默认每一条线程占512KB,OS X中8MB)
-
线程数量多时,CPU在调用线程上的开销大
-
线程数量多时,程序设计更复杂,需要考虑线程间通信,多线程数据共享等
iOS中的多线程技术方案

线程调度
线程生命周期

线程池调度策略
其中线程的运行与否取决于线程池的调度,调度策略如下图所示:

其中饱和策略可能有几种情况
-
AbortPolicy:直接抛出RejectedExecutionException异常来阻止系统正常运行 -
DisOldestPolicy:丢掉等待最久的任务 -
CallerRunsPolicy:将任务回退到调用者 -
DiscardPolicy:直接丢弃任务
任务执行速率与线程优先级分析
任务是依赖线程执行的,所以在一条线程中,影响任务执行速度的因素如下
- CPU的硬件条件+调度情况
- 任务的复杂度
- 任务的优先级
- 线程状态
线程优先级反转
首先我们要知道,线程有如下两种,IO密集型比CPU密集型更容易获得优先级提升
-
IO密集型:频繁等待
-
CPU密集型:很少等待
注意:优先级提升不代表就会执行
所以线程优先级的影响因素如下:
- 用户指定
- 等待的频繁度
- 长时间不执行 -> 提高优先级
应用
属性的多读单写
- 读写锁
pthread_rwlock
@interface YKThread () {
pthread_rwlock_t rwlock;
}
@property (nonatomic, strong) NSMutableArray *list;
@end
@implementation YKThread
static NSMutableArray * _list;
- (instancetype)init {
if (self = [super init]) {
self.list = [[NSMutableArray alloc] init];
pthread_rwlock_init(&rwlock,
NULL);
}
return self;
}
- (NSMutableArray *)list {
//加读锁
pthread_rwlock_rdlock(&rwlock);
NSMutableArray *result = _list;
pthread_rwlock_unlock(&rwlock);
return result;
}
- (void)setList:(NSMutableArray *)list {
//加写锁
pthread_rwlock_wrlock(&rwlock);
_list = [list mutableCopy]; // 注意这里是浅拷贝,如有需要可实现深拷贝相关方法,这里不为重点
pthread_rwlock_unlock(&rwlock);
}
-
栅栏函数
dispatch_sync读+dispatch_barrier_sync写⚠️仅适用于对外部调用顺序没有要求!
- dispatch_sync读:并发同步读取数据,能保证外部调用顺序。此时会堵塞当前线程,当前线程需要等待读取任务执行完成,才能继续执行后边代码任务。
- dispatch_barrier_sync 写:并发异步回调方式读取数据,当你对外部调用顺序没有要求时,可以这么调用。不需要等待写操作完成,所以用异步。
@interface YKThread () {
dispatch_queue_t concurrentQueue;
}
@property (nonatomic, strong) NSMutableArray *list;
@end
@implementation YKThread
static NSMutableArray * _list;
- (instancetype)init {
if (self = [super init]) {
self.list = [[NSMutableArray alloc] init];
concurrentQueue = dispatch_queue_create("com.yk.barrier.queue",
DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
- (NSMutableArray *)list {
__block NSMutableArray *result;
dispatch_sync(concurrentQueue, ^{
result = _list;
});
return result;
}
- (void)setList:(NSMutableArray *)list {
dispatch_barrier_async(concurrentQueue,
^{
_list = list;
});
}
最大并发数
NSOperationQueue
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
queue.maxConcurrentOperationCount = 3;
- 信号量
dispatch_semaphore_t
- (void)maxConcurrent {
int concurrentCount = 2;
dispatch_semaphore_t sem = dispatch_semaphore_create(concurrentCount);
for (int i = 0; i < 100; i++) {
// dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);放在这里
// 最大并发数为concurrentCount
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(2 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
NSLog(@"Task %d",i);
dispatch_semaphore_signal(sem);
});
// 若dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);放在这里
// 则最大并发数为(concurrentCount + 1)
}
}