1. iOS中的多线程技术
-
pthread pthread是一套同样的多线程API, 在Unix/Linux等系统都可以使用
优点: 跨平台, 可移植
缺点: 非常底层, 使用难度大(需要扎实的技术) -
NSThread NSThread是iOS中基于OC提供的多线程API
优点: 使用面向对象, 简单易用, 可以直接操作线程对象
缺点: 使用起来没有GCD快速, 需要声明对象, 调用对象方法, 需要自己管理线程的生命周期 -
GCD 基于C语言, 旨在代替NSThread的多线程技术
优点: 系统自动管理生命周期, 使用简单(Block直接使用), 系统底层会充分利用CPU多核
缺点: 不能手动管理生命周期 -
NSOperation 基于GCD面向对象的封装
优点: 基于GCD, 使用面向对象, 可以操作线程的启动取消暂停, 可以添加线程依赖, 获取线程的状态如 finished/cancelled等
缺点: 使用起来没有GCD快速, 需要声明对象
2. 同步, 异步, 串行, 并行
-
同步和异步主要是否开启新的线程执行任务
同步: 在当前线程执行任务, 不开启新线程
异步: 开启新线程, 在新线程执行任务 -
串行和并行主要描述执行任务的方式 串行: 多个任务依次执行
并行: 多个任务并发执行
使用 sync(同步方法)在串行队列添加任务, 会导致死锁
3.多线程同步和锁
多线程的使用是有危险的, 当多个线程同时对一个资源进行读写, 很容易造成数据的错乱. 解决方案就是多线程同步, 是多个线程按照先后顺序同步执行. 常用的手段是加锁
-
OSSpinLock: 自旋锁 自旋锁在等待锁时会处于忙等的状态, 一直占用CPU资源. 可以理解为一个while死循环
-
os_unfair_lock os_unfair_lock用于取代不安全的OSSpinLock, 在底层等待锁时, CPU会进入休眠, 而不是忙等
-
pthread_mutex: 互斥锁 互斥锁的等待锁会处于休眠状态, 使用 PTHREAD_MUTEX_RECURSIVE type初始化可以解决递归锁的问题
-
NSLock: 对pthread_mutex的封装
-
NSRecursiveLock: 对pthread_mutex递归锁的封装
-
dispatch_semaphore: 信号量 当信号量的值<=0时, 当前线程就会进入休眠等待, 当信号量 >0 时会执行wait()后的代码; 通过初始化信号量来控制线程并发的最大数量. dispatch_semaphore_wait(): 使信号量 -1 dispatch_semaphore_signal(): 使信号量 +1
-
dispatch_queue: GCD dispatch_queue在初始化时, 可以选择使用串行队列, 那么在队列中的任务就会同步执行
-
@synchronized 对pthread_mutex递归锁的封装
-
自旋锁和互斥锁 自旋锁: 等待锁时会一直占用CPU资源, 处于忙等. 因为会一直占用CPU资源, 所以适合等待锁时间短, CPU资源不紧张时使用.
互斥锁: 等待锁时会进入休眠, 一直等待唤醒. 因为休眠唤醒需要更多的资源, 所以适合用在等待锁时间比较长的场景. -
dispatch_barrier_async/dispatch_barrier_sync: 线程栅栏 使用线程栅栏可以将栅栏前后的任务进行分割, 保证栅栏前边的任务执行完成后再执行栅栏后的任务
4.多线程的使用场景
- 实现多线程卖票: 多个线程同时卖票, 要保证票的数量正确
// 设定初始值为10, 开启3个线程进行卖票
- (void)ticketTest {
self.count = 10;
self.ticketLock = [[NSLock alloc] init];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
[self saleTicket];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
[self saleTicket];
}
});
}
// 在开始时对线程进行加锁, 处理完成以后进行解锁保证线程同步
- (void)saleTicket {
[self.ticketLock lock];
int tempCount = self.count;
sleep(.3);
tempCount--;
self.count = tempCount;
[self.ticketLock unlock];
NSLog(@"还剩%d张票 ", self.count);
}
- 设定有A, B, C, D 4个任务, 要求C, D任务要在A, B完成以后在进行
- GCD的group
// 定义group 和 queue
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
NSLog(@"A");
});
dispatch_group_async(group, queue, ^{
NSLog(@"B");
sleep(1); // 假设B任务延迟执行完成
});
// 在group完成的notify中执行任务C, D
dispatch_group_notify(group, queue, ^{
NSLog(@"C, D");
});
- NSOperation设置依赖
// 创建queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *opA = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"A");
}];
NSBlockOperation *opB = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"B");
sleep(1); // 假设B任务延迟执行完成
}];
NSBlockOperation *opCD = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"C, D");
}];
// 设置B依赖于A, CD依赖于B
[opB addDependency:opA];
[opCD addDependency:opB];
// 将任务加入队列执行
[queue addOperation:opA];
[queue addOperation:opB];
[queue addOperation:opCD];
使用NSOperation则必须同时设置多个依赖才可以完成
- dispatch_barrier_async线程栅栏
// 创建队列
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"A");
});
dispatch_async(queue, ^{
NSLog(@"B");
sleep(1); // 假设B任务延迟执行完成
});
// 设置线程栅栏
dispatch_barrier_async(queue, ^{
NSLog(@"barrier");
});
dispatch_async(queue, ^{
NSLog(@"C");
});
dispatch_async(queue, ^{
NSLog(@"D");
});