一、多线程方案
默认情况下,主线程占用1M,子线程占用512KB
- NSThread 面向对象的,手动创建线程,不需要手动销毁。子线程间通信很难。
- GCD c语言,充分利用了设备的多核,自动管理线程生命周期。比NSOperation效率更高。
- NSOperation 基于gcd封装,更加面向对象,比gcd多了一些功能。
GCD和NSOperation的对比:
- GCD执行效率更高,而且由于队列中执行的是由block构成的任务,这是一个轻量级的数据结构——写起来更加方便。
- GCD只支持FIFO的队列,需要通过信号量才能达到并发。
- NSOpration可以设置最大并发数(maxConcurrentOperationCount)、设置优先级、添加依赖关系等调整执行顺序
- NSOpration甚至可以跨队列设置依赖关系,但是GCD只能通过设置串行或者并行队列,或者在队列内添加barrier任务才能控制执行顺序,较为复杂
- NSOperation支持KVO(面向对象)可以检测operation是否正在执行、是否结束、是否取消
GCD执行原理:
GCD有一个底层线程池,这个池中存放的是一个个的线程。“池”中的线程是可以重用的,当一段时间后这个线程没有被调用胡话,这个线程就会被销毁。
NSOperation:
NSInvocationOperation 程序在主线程执行,没有开启新线程。需要配合队列NSOperationQueue。 NSBlockOperation 程序在主线程执行,没有开启新线程。需要配合队列NSOperationQueue。 NSOperation 子类重写它的main方法,是需要配合队列NSOperationQueue来实现多线程的。
NSOperationQueue:
主队列、其他队列。 主队列上的任务是在主线程执行
1、非主队列(其他队列)可以实现串行或并行。
2、队列NSOperationQueue有一个参数叫做最大并发数,maxConcurrentOperationCount默认为-1,直接并发执行,所以加入到‘非队列’中的任务默认就是并发,开启多线程。当maxConcurrentOperationCount为1时,则表示不开线程,也就是串行。当maxConcurrentOperationCount大于1时,进行并发执行。
NSThread和NSOperationQueue开辟子线程需要手动创建autoreleasepool,GCD开辟子线程不需要手动创建autoreleasepool,因为GCD的每个队列都会自行创建autoreleasepool
二、线程安全
1、NSLock
iOS 多线程开发中为保证线程安全而常用的几种锁:NSLock、dispatch_semaphore、NSCondition、NSRecursiveLock、NSConditionLock、@synchronized`,
这几种锁各有优点,适用于不同的场景,下面我们就来依次介绍一下。
什么情况使用自旋锁比较划算?
预计线程等待锁的时间很短 加锁的代码(临界区)经常被调用,但竞争情况很少发生 CPU资源不紧张 多核处理器
什么情况使用互斥锁比较划算?
预计线程等待锁的时间较长 单核处理器 临界区有IO操作 临界区代码复杂或者循环量大 临界区竞争非常激烈
2、信号量
// 根据一个初始值创建信号量
dispatch_semaphore_create(信号量值)
// 如果信号量的值<=0,当前线程就会进入休眠等待(直到信号量的值>0);如果信号量的值>0,就减1,然后往下执行后面的代码。
dispatch_semaphore_wait(信号量,等待时间)
// 提高信号量(让信号量的值加1)
dispatch_semaphore_signal(信号量)
dispatch_group_enter(请求前)
dispatch_group_leave(请求回调后)
dispatch_group_notify
信号量的销毁会调用_dispatch_semaphore_dispose函数,而此函数会执行信号当前值与初始化值的比较,如果小于初始化值,则直接抛出崩溃。
3、栅栏函数
- dispatch_barrier_async:阻塞了队列执行,不阻塞线程执行
- dispatch_barrier_sync:阻塞了队列执行,阻塞线程执行
栅栏函数的读写锁: 读:dispatch_sync (并发同步) 写:dispatch_barrier_async
4、串行队列
并行才是真正的多线程,而并发只是在多任务中切换。一般多核CPU可以并行执行多个线程,而单核CPU实际上只有一个线程,多路复用达到接近同时执行的效果。
5、同步函数
dispatch_sync
三、死锁
1、同步线程往串行队列中添加任务(主线程里同步执行任务) 主队列中追加的同步任务 和 主线程本身的任务 两者之间相互等待,阻塞了 『主队列』,最终造成了主队列所在的线程(主线程)死锁问题
2、NSOperationQueue通过addDependency循环从属或者互相从属
3、NSLock已上锁的情况下再次上锁,形成彼此等待的局面(NSLock换成递归锁NSRecursiveLock)
四、典型的“多读单写”
pthread_rwlock:读写锁 dispatch_barrier_async:异步栅栏调用
五、dispatch_semaphore_t
控制线程并发数
//定义一个信号量,初始化为10
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
//同时执行100个任务
for (int i = 0; i < 100; I++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//当前信号量-1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"任务%d执行",i+1);
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kUrlString]];
dispatch_async(dispatch_get_main_queue(), ^{
//TODO:刷新界面
});
//当前信号量+1
dispatch_semaphore_signal(semaphore);
});
}
线程安全
初时值为1时,就可以实现线程同步。
- (void)viewDidLoad {
[super viewDidLoad];
semaphore = dispatch_semaphore_create(1);
self.array = [NSMutableArray array];
for (int i=0; i<100; i++) {
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(test) object:nil];
[thread start];
}
}
- (void)test {
NSLog(@"测试开始");
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
sleep(3);
[self.array addObject:@"0"];
dispatch_semaphore_signal(semaphore);
NSLog(@"测试");
}
多个网络请求同步
首先通过网络请求一获取用户useid,之后用userid为参数发起网络请求二。
- (void)viewDidLoad {
[super viewDidLoad];
//创建一个并行队列
dispatch_queue_t queque = dispatch_queue_create("GoyakodCreated", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queque, ^{
dispatch_semaphore_t semaphore= dispatch_semaphore_create(0); // 创建信号量
[self getuserId:semaphore];
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
[self requestwithuserid:useid];
});
}
pragma mark - 网络请求一
- (void)getuserId:(dispatch_semaphore_t)semaphore {
AFHTTPSessionManager *sessionmanger=[[AFHTTPSessionManager alloc]init];
sessionmanger.responseSerializer=[AFHTTPResponseSerializer serializer];
[sessionmanger POST:@"[https://www.baidu.com/](https://www.baidu.com/)" parameters:nil constructingBodyWithBlock:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"请求成功1%@", [NSThread currentThread]);
useid=@"1234";
dispatch_semaphore_signal(semaphore);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"%@",error);
dispatch_semaphore_signal(semaphore);
}];
}
pragma mark - 网络请求二
- (void)requestwithuserid:(NSString *)userid {
NSDictionary *parms=[NSMutableDictionary dictionary];
[parms setValue:userid forKey:@"userid"];
AFHTTPSessionManager *sessionmanger=[[AFHTTPSessionManager alloc]init];
sessionmanger.responseSerializer=[AFHTTPResponseSerializer serializer];
[sessionmanger POST:@"[https://www.baidu.com/](https://www.baidu.com/)" parameters:userid constructingBodyWithBlock:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"请求成功2");
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
多个网络请求后刷新UI
dispatch_group_t downloadGroup = dispatch_group_create();
for (int i=0; i<10; i++) {
dispatch_group_enter(downloadGroup);
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_group_leave(downloadGroup);
}];
[task resume];
}
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
NSLog(@"end");
});
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
for (int i=0; i<10; i++) {
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
count++;
if (count==10) {
dispatch_semaphore_signal(sem);
count = 0;
}
}];
[task resume];
}
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
});
多个网络请求顺序执行后执行下一步
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
for (int i=0; i<10; i++) {
NSURLSessionDataTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_semaphore_signal(sem);
}];
[task resume];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"end");
});
异步操作两组数据时, 执行完第一组之后, 才能执行第二组
- 这里使用dispatch_barrier_async栅栏方法即可实现
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"第一次任务的主线程为: %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"第二次任务的主线程为: %@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"第一次任务, 第二次任务执行完毕, 继续执行");
});
dispatch_async(queue, ^{
NSLog(@"第三次任务的主线程为: %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"第四次任务的主线程为: %@", [NSThread currentThread]);
});