https://www.cnblogs.com/fengmin/p/5841014.html参考源码解读
[图片上传中...(截屏2019-12-15下午11.26.52.png-95caa7-1576423615938-0)]
一.线程间通信以及各自的特点
通信
// 1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
// 2.添加操作
[queue addOperationWithBlock:^{
// 异步进行耗时操作
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
}
// 回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// 进行一些 UI 刷新等操作
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
}
}];
}];
GCD
performSelectorInbackground:
performSelectorOnMainThread
二.比较:
NSOperation底层是GCD更加面向对象,是可以获得队列执行状态,设置线程依赖,线程优先级,可以取消线程,可以控制最大并发量。
GCD更加简单高效。
二.NSOperation
1,控制最大并发数
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 设置最大并发操作数
// queue.maxConcurrentOperationCount = 2;
queue.maxConcurrentOperationCount = 1; // 就变成了串行队列
// 添加操作
[queue addOperationWithBlock:^{
NSLog(@"1-----%@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];
[queue addOperationWithBlock:^{
NSLog(@"2-----%@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];
2,最吸引人的地方是它能添加操作之间的依赖关系
比如说有A、B两个操作,其中A执行完操作,B才能执行操作,那么就需要让B依赖于A
//操作依赖
-
(void)addDependency
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1-----%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2-----%@", [NSThread currentThread]);
}];[op2 addDependency:op1]; // 让op2 依赖于 op1,则先执行op1,在执行op2
[queue addOperation:op1];
[queue addOperation:op2];
}
//可以看到,无论运行几次,其结果都是op1先执行,op2后执行。
三.队列的取消、暂停、和恢复
取消队列的所有操作
-(void)cancelAllOperations;
提示:也可以调用NSOperation的-(void)cancel方法取消单个操作。
暂停和恢复队列
-(void)setSuspended:(BOOL)b; // YES表示暂停队列 NO表示恢复队列
-(BOOL)isSuspend;
获取队列操作数:
operationCount(只读属性)
注意:
(1)暂停不会删除队列内的操作。只是把队列挂起。暂停和挂起都是针对队列而言的。暂停后还可以重新恢复接着原来的任务进行执行。
(2)取消全部任务的操作会清空队列里的所有任务。
(3)暂停和取消都是对队列里的操作而言的,而正在执行的操作是无法取消或暂停的。
三 .GCD
dispatch_sync和dispatch_async用来控制是否要开启新的线程
队列的类型,决定了任务执行的方式(串行,并发)(dispatch_get_main_queue()书串行队列)
还有一种情况,先并发同时执行任务1,任务二,等他俩执行完,再通知任务三任务四并发执行。就是下面的截图代码。两个notify被通知到,同时在queue这个并发队列里去执行任务即可!
dispatch_group_enter :通知 group,下个任务要放入 group 中执行了
dispatch_group_leave: 通知 group,任务成功完成,要移除,与 enter成对出现
dispatch_group_wait: 在任务组完成时调用,或者任务组超时是调用(完成指的是enter和leave次数一样多)
dispatch_group_notify: 只要任务全部完成了,就会在最后调用
- (void)test {
NSURL *url = [NSURL URLWithString:@"http://upload-images.jianshu.io/upload_images/1432482-dcc38746f56a89ab.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"];
SDWebImageManager *manager = [SDWebImageManager sharedManager];
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[manager loadImageWithURL:url options:SDWebImageRefreshCached progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"下载完成了");
});
}
//信号量 也可以用信号量的方式去做
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
//创建全局并行
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, ^{
[self getAdvertList:^(BOOL iscomple) {
dispatch_semaphore_signal(semaphore);
}];
});
//任务二
dispatch_group_async(group, queue, ^{
[self getHotCultureList:^(BOOL iscomple) {
dispatch_semaphore_signal(semaphore);
}];
});
dispatch_group_notify(group, queue, ^{
//6个任务,6个信号等待.
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//这里就是所有异步任务请求结束后执行的代码
//[self.homeTableView.mj_header endRefreshing];
//这里两个网络请求结束后。获取到的。一个参数用于第三个借口参数
NSLog(@"------------4444444444444444-xin hao-------%@", self.imeiStr);
多线程安全
不是所有多线程都加锁,如果只是读取数据,那么不会对数据造成安全隐患,当多个线程对数据同时读写的时候才需要加锁保证线程安全
锁:
(1)OSSpinLock 自旋锁(high level高级锁 不休眠)
(2) os_unfair_lock(它是互斥锁)
(3)pthread_mutex
生产者-消费者模式:同时进行的,但是需要先生产出产品才可以卖给消费者
总结:
- (instancetype)init
{
if (self = [super init]) {
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// 初始化锁
pthread_mutex_init(&_mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
// 初始化条件
pthread_cond_init(&_cond, NULL);
self.data = [NSMutableArray array];
}
return self;
}
- (void)otherTest
{ //子线程同时进行 所以不确定先调用哪个方法
[[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}
// 生产者-消费者模式
// 线程1
// 删除数组中的元素
- (void)__remove
{
pthread_mutex_lock(&_mutex);
NSLog(@"__remove - begin");
if (self.data.count == 0) {
// 等待 ⚠️⚠️⚠️等待期间会放开这把锁,这个是可能就会调用添加方法给它加锁,当添加完调用signal,就会唤醒加这个条件的锁的线程,注意⚠️其实这个时候是继续等待状态等待别人放开这把锁,也是等添加方法执行unlock解开这个锁的时候,pthread_cond_wait它才可以再次加锁,就继续往下走去remove,执行完了解开这个锁。
pthread_cond_wait(&_cond, &_mutex);
}
[self.data removeLastObject];
NSLog(@"删除了元素");
pthread_mutex_unlock(&_mutex);
}
// 线程2
// 往数组中添加元素
- (void)__add
{
pthread_mutex_lock(&_mutex);//初始化锁
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
// 信号 激活这个等待条件的线程
pthread_cond_signal(&_cond);
// 广播
// pthread_cond_broadcast(&_cond);
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc
{ //销毁资源
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
(4)NSLock,NSRecursiveLock。NSRecursiveLock递归锁表示可处理同一方法内部多次上锁,表示它可以允许同一线程对其多次加锁,而不会引起死锁的问题,主要用在循环和递归调用中。也是基于mutex实现的,所以效率肯定低于mutex
(5)NSCondition
@interface NSConditionDemo()
@property (strong, nonatomic) NSCondition *condition;
@property (strong, nonatomic) NSMutableArray *data;
@end
@implementation NSConditionDemo
- (instancetype)init
{
if (self = [super init]) {
self.condition = [[NSCondition alloc] init];
self.data = [NSMutableArray array];
}
return self;
}
- (void)otherTest
{
[[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}
// 生产者-消费者模式
// 线程1
// 删除数组中的元素
- (void)__remove
{
[self.condition lock];
NSLog(@"__remove - begin");
if (self.data.count == 0) {
// 等待
[self.condition wait];
}
[self.data removeLastObject];
NSLog(@"删除了元素");
[self.condition unlock];
}
// 线程2
// 往数组中添加元素
- (void)__add
{
[self.condition lock];
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
// 信号
[self.condition signal];
// 广播
// [self.condition broadcast];
[self.condition unlock];
}
注意
(6)NSConditionLock
(6)dispatch_queue
(6) dispatch_semaphore
注意:wait和signal的顺序。当创建时value为0时那么需要先signal+1,然后再wait。注意使用顺序和信号量的关系
• dispatch_semaphore_create(value)
创建信号量,value一般情况下传0
• dispatch_semaphore_wait()
等待信号量,会对信号量减1(value - 1),当信号量 < 0 时,会阻塞当前线程,等待信号(signal),当信号量 >= 0时,会执行wait后面的代码
• dispatch_semaphore_signal()
信号量加1,当信号量 >= 0 会执行wait之后的代码。
因此dispatch_semaphore_wait()和dispatch_semaphore_signal()要成对使用。
(7)synchronized