一、死锁
- 最容易导致死锁的一个情况
信号锁 + 互斥锁 ,没处理好,导致死锁,NSConditionLock 解决死锁问题。
例1:互斥锁执行两次lock操作[self.lock lock];
例2:信号锁+互斥锁
@interface MyLock()
@property(nonatomic, strong) NSLock * lock;
@property (nonatomic, strong) dispatch_semaphore_t semaphore;
@property (nonatomic, assign) NSInteger count;
@end
@implementation MyLock
- (void)dealloc{
NSLog(@"%s",__func__);
}
- (instancetype)init{
self = [super init];
if (self) {
//创建线程
self.lock = [[NSLock alloc] init];
[NSThread detachNewThreadSelector:@selector(signalWrite) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(signalRead) toTarget:self withObject:nil];
self.semaphore = dispatch_semaphore_create(0);
}
return self;
}
- (void)signalWrite{
while (1) {
[self.lock lock];
NSLog(@"%ld",self.count);
if (self.count >= 10) {
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
}
self.count ++;
NSLog(@"%s",__func__);
[self.lock unlock];
}
}
- (void)signalRead{
while (1) {
[self.lock lock];
NSLog(@"%ld",self.count);
if (self.count >= 10) {
self.count --;
dispatch_semaphore_signal(_semaphore);
}else{
self.count ++;
}
NSLog(@"%s",__func__);
[self.lock unlock];
}
}
- 使用条件锁解决死锁问题 信号锁+互斥锁 = 条件锁
@interface MyConditionLock()
@property (nonatomic,strong) NSConditionLock * conditionLock;
@property (nonatomic, assign) NSInteger count;
@property (nonatomic, strong) NSCondition * condition;
@end
@implementation MyConditionLock
- (instancetype)init{
self = [super init];
if (self) {
self.condition = [[NSCondition alloc] init];
[NSThread detachNewThreadSelector:@selector(thread1) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(thread2) toTarget:self withObject:nil];
self.count = 0;
}
return self;
}
- (void)thread1{
while (1) {
[self.condition lock];
if (self.count >= 10) {
//这里的wait相当于释放了锁
[self.condition wait];
}
self.count ++;
NSLog(@"%s = %ld",__func__,self.count);
[self.condition unlock];
}
}
- (void)thread2{
while (1) {
[self.condition lock];
if (self.count >= 10) {
self.count --;
[self.condition signal];
}else{
self.count ++;
}
NSLog(@"%s = %ld",__func__,self.count);
[self.condition unlock];
}
}
@end
- 条件锁原理
@interface ConditionTheory(){
pthread_mutex_t mutex_t;
pthread_cond_t cond_t;
int _count;
}
@end
@implementation ConditionTheory
- (instancetype)init{
self = [super init];
if (self) {
pthread_mutex_init(&mutex_t,0);
pthread_cond_init(&cond_t, 0);
[NSThread detachNewThreadSelector:@selector(threadOne) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(threadTwo) toTarget:self withObject:nil];
}
return self;
}
#pragma mark - C用法条件量 (原理)
- (void)threadOne{
while (1) {
pthread_mutex_lock(&mutex_t);
NSLog(@"signalLockWrite lock");
if (_count >= 10) {
NSLog(@"空间满了,等待中--%d", _count);
// [conditionLock wait];
pthread_cond_wait(&cond_t, &mutex_t);
}
_count++;
pthread_mutex_unlock(&mutex_t);
NSLog(@"signalLockWrite unlock");
}
}
- (void)threadTwo{
while (1) {
NSLog(@"signalLockRead lock");
pthread_mutex_lock(&mutex_t);
if (_count > 10) {
_count--;
NSLog(@"释放空间~");
// [conditionLock signal];
pthread_cond_signal(&cond_t);
}else {
_count++;
}
NSLog(@"signalLockRead lock");
pthread_mutex_unlock(&mutex_t);
}
}
二、多线程GCD
1、main_dispatch_queue(系统提供的)
获取主队列
全局性的串性队列,所有的ui操作相关的任务都应该放在这个queue里面,在主线程中执行通过宏dispatch_get_main_queque()
创建任务
2、globlal_dispatch_queue(系统提供的)
并发执行多个任务队列,但是执行完成的顺序是随机的。一般后台执行的任务放在这个队列里面。
获取队列,通过函数来设置任务的优先级:dispatch_get_global_queue(0,0)
,第一个参数,为默认的优先级(四个优先级),第二个参数为预留的。
3、自定义的dispatch queque(可以是串型队列,也可以是并行队列)
创建串型队列,自定义的queue一般和ui操作无关,用于同步访问特定的资源或数据,比如多线程对同一文件的写入。
dispatch_queue_create("SerialQueue",DISPATCH_QUEUE_SERIAL)
,第一个是队列名称,第二个参数为串形队列。DISPATCH_QUEUE_CONCURRENT
并形队列
4、提交任务到dispatch_queue
block 里面是任务
- 同步任务
void dispatch_sync(dispatch_queue_t queue,dispatch_block_t block);
- 异步任务
void dispatch_async(dispatch_queue_t queue,dispatch_block_t block);
将耗时的操作提交给非主线程,更新界面提交给主线程,如下:
dispatch_async(dispatch_get_golabal_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
//耗时操作,解析数据
dispatch_async(dispatch_get_main_queue(),^{
//更新界面
});
});
5、组队列
- 组队列通知,用于组队列全部安全产生的回掉。
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue1, ^{
NSLog(@"one_task");
sleep(1);
NSLog(@"one_task over");
});
dispatch_group_async(group, queue1, ^{
NSLog(@"2_task");
sleep(1);
NSLog(@"2_task over");
});
dispatch_group_async(group, queue1, ^{
NSLog(@"3_task");
sleep(1);
NSLog(@"3_task over");
});
dispatch_group_notify(group, queue1, ^{
//当所有队列完成是,才会走这里
NSLog(@"nofify no task");
});
dispatch_group_async(group, queue1, ^{
NSLog(@"4_task");
sleep(1);
NSLog(@"4_task over");
});
- AFNetworking 中会有类似的应用。
向组队列中添加空任务:dispatch_group_enter(group);
,但一定要移除:dispatch_group_leave(group)
。否则不会执行dispatch_group_notify
。
6、设置屏障
- 应用场景:当某些任务完成之后才执行另外一条任务。
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"one tast");
sleep(1);
NSLog(@"one task finished");
});
dispatch_async(queue, ^{
NSLog(@"two tast");
sleep(1);
NSLog(@"two task finished");
});
//设置屏障
dispatch_barrier_async(queue, ^{
NSLog(@"barrier");
});
dispatch_async(queue, ^{
NSLog(@"three tast");
sleep(1);
NSLog(@"three task finished");
});
运行结果
one tast
two tast
one task finished
two task finished
barrier
three tast
three task finished
7、应用多次,多次迭代
- 并行应用多次,系统随机执行任务
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(5, queue, ^(size_t count) {
NSLog(@"%d==%@",(int)count,[NSThread currentThread]);
});
运行结果:
0==<NSThread: 0x60c000078580>{number = 1, name = main}
2==<NSThread: 0x604000468540>{number = 4, name = (null)}
1==<NSThread: 0x608000263900>{number = 3, name = (null)}
3==<NSThread: 0x604000468600>{number = 5, name = (null)}
4==<NSThread: 0x60c000078580>{number = 1, name = main}
- 串型运行多次
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_apply(5, queue, ^(size_t count) {
NSLog(@"%d==%@",(int)count,[NSThread currentThread]);
});
运行结果:
0==<NSThread: 0x60c00007b4c0>{number = 1, name = main}
1==<NSThread: 0x60c00007b4c0>{number = 1, name = main}
2==<NSThread: 0x60c00007b4c0>{number = 1, name = main}
3==<NSThread: 0x60c00007b4c0>{number = 1, name = main}
4==<NSThread: 0x60c00007b4c0>{number = 1, name = main}
8、延时
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2.2*NSEC_PER_SEC);
dispatch_after(time, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSRunLoop * runLoop = [NSRunLoop currentRunLoop];
NSLog(@"runloop = %@",runLoop);
[self performSelect];
});
9、计时器
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//1秒钟开始一次,误差是一秒秒
dispatch_source_set_timer(source, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC , 1 * NSEC_PER_SEC);
//设置事件
dispatch_source_set_event_handler(source, ^{
NSLog(@"%@",[NSThread currentThread]);
});
dispatch_resume(source);
三、注意:
- 1、在主线程开主线串行队列,会导致奔溃。
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_sync(dispatch_get_main_queue(), ^{
// //会等待主线程完成,而主线程不会完成
NSLog(@"dispatch_sync");
});
[self time];
}
- 2、dispatch_sync的官方文档中有这么一句话:
As an optimization, this function invokes the block on the current thread when possible.
意思是作为优化手段,如果可以,block会直接在当前线程中执行。
然后写了几段代码测试了下:
dispatch_async(_myQueue1, ^{
NSLog(@"1 start!");
sleep(1);
NSLog(@"1 stop!");
dispatch_async(_myQueue2, ^{
NSLog(@"2 start!");
sleep(2);
NSLog(@"2 stop!");
});
dispatch_sync(_myQueue2, ^{
NSLog(@"3 start!");
sleep(1);
NSLog(@"3 stop!");
});
});
如果_myQueue1,_myQueue2都是自己创建的队列,那3确实是与1在同一线程中执行,2在另外一个线程中执行。但是3虽然与2不在一个线程中,但是由于在同一队列里,所以依然要等待2执行完才能执行3,当前线程会被阻塞直到2、3都执行完。
如果令_myQueue1 = dispatch_get_main_queue(),_myQueue2是自己创建的队列,3就会在主线程中执行。也就是说在主线程中调用dispatch_sync往子线程派发任务,任务会直接在主线程执行。
但如果_myQueue1是自己创建的队列,_myQueue2 = dispatch_get_main_queue(),也就是说在子线程中调用dispatch_sync往主线程派发任务,GCD不会做这种优化,任务会如你所愿在主线程中执行。
以上结论仅仅是简单代码测试得到,有可能在复杂环境下苹果有更为复杂的线程调度策略。大家在使用同步派发时需要自己注意下。
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t _myQueue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t _myQueue1 = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(_myQueue1, ^{
NSLog(@"1 start!");
sleep(1);
NSLog(@"1 stop!");
dispatch_async(_myQueue2, ^{
NSLog(@"2 start!");
sleep(2);
NSLog(@"2 stop!");
});
dispatch_sync(_myQueue2, ^{
NSLog(@"3 start!");
sleep(12);
NSLog(@"3 stop!");
});
});
}
结果:
2018-04-19 00:09:57.249 Demo1[2096:799609] 1 start!
2018-04-19 00:09:58.254 Demo1[2096:799609] 1 stop!
2018-04-19 00:09:58.254 Demo1[2096:799609] 3 start!
2018-04-19 00:09:58.254 Demo1[2096:799588] 2 start!
2018-04-19 00:10:00.260 Demo1[2096:799588] 2 stop!
2018-04-19 00:10:10.255 Demo1[2096:799609] 3 stop!
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t _myQueue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t _myQueue1 = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(_myQueue1, ^{
NSLog(@"1 start!");
sleep(1);
NSLog(@"1 stop!");
dispatch_sync(_myQueue2, ^{
NSLog(@"2 start!");
sleep(6);
NSLog(@"2 stop!");
});
dispatch_async(_myQueue2, ^{
NSLog(@"3 start!");
sleep(2);
NSLog(@"3 stop!");
});
});
}
结果:
2018-04-18 23:32:44.521 Demo1[1936:681182] 1 start!
2018-04-18 23:32:45.527 Demo1[1936:681182] 1 stop!
2018-04-18 23:32:45.527 Demo1[1936:681182] 2 start!
2018-04-18 23:32:51.530 Demo1[1936:681182] 2 stop!
2018-04-18 23:32:51.530 Demo1[1936:681182] 3 start!
2018-04-18 23:32:53.532 Demo1[1936:681182] 3 stop!
网友评论