场景
同步提交到主队列
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"同步提交到主队列");
});
}
死锁,崩溃。
原因: 主队列在执行viewDidLoad任务,又同步执行block任务;
viewDidLoad任务等待block任务结束;block任务又在等待viewDidLoad任务。
造成主队列的相互等待,并非主线程死锁。
自定义串行队列
dispatch_queue_t queue_t = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue_t, ^{
NSLog(@"异步提交到串行队列");
dispatch_sync(queue_t, ^{
NSLog(@"同步提交到串行队列");
});
});
死锁,崩溃。原因与在主线程同步提交主队列原因相同。由于串行队列的相互等待造成死锁。
队列改成并发,可以正常执行。
如何避免死锁
主线程中可以[NSThread isMainThread]判断:
if ([NSThread isMainThread]) {
block();
} else {
dispatch_async(dispatch_get_main_queue(), ^{
block();
});
}
自定义队列需要使用dispatch_queue_set_specific与dispatch_get_specific。
dispatch_queue_t queue_t = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_set_specific(queue_t, &QueueIdentityKey, (__bridge void *)queue_t, NULL);
dispatch_async(queue_t, ^{
NSLog(@"异步提交到串行队列");
if (queue_t != dispatch_get_specific(&QueueIdentityKey)) {
dispatch_sync(queue_t, ^{
NSLog(@"同步提交到串行队列");
});
}else{
NSLog(@"已经是串行队列任务,不要在同步提交了");
}
});
实际场景
DB多读单写的实现
@interface BLDBQueueManager()
@property (nonatomic, strong) dispatch_queue_t dbConcurrentQueue;
@end
@implementation BLDBQueueManager
+ (BLDBQueueManager *)shareInstance{
static BLDBQueueManager *_instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!_instance) {
_instance = [[self alloc] init];
}
});
return _instance;
}
- (instancetype)init{
self = [super init];
if (self) {
_dbConcurrentQueue = dispatch_queue_create("DBQueue", DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
- (void)asyncBarrierDBBlock:(void(^)(void))block{
dispatch_barrier_async(self.dbConcurrentQueue, ^{
block();
});
}
- (void)syncDBBlock:(void(^)(void))block{
dispatch_sync(self.dbConcurrentQueue, ^{
block();
});
}
上层业务异步获取结果再调同步也会死锁。
修订版
@interface BLDBQueueManager()
{
void *QueueIdentityKey;
}
@property (nonatomic, strong) dispatch_queue_t dbConcurrentQueue;
@end
@implementation BLDBQueueManager
+ (BLDBQueueManager *)shareInstance{
static BLDBQueueManager *_instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!_instance) {
_instance = [[self alloc] init];
}
});
return _instance;
}
- (instancetype)init{
self = [super init];
if (self) {
_dbConcurrentQueue = dispatch_queue_create("DBQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_set_specific(_dbConcurrentQueue, &QueueIdentityKey, (__bridge void *)_dbConcurrentQueue, NULL);
}
return self;
}
- (void)asyncBarrierDBBlock:(void(^)(void))block{
dispatch_barrier_async(self.dbConcurrentQueue, ^{
block();
});
}
- (void)syncDBBlock:(void(^)(void))block{
if (self.dbConcurrentQueue != dispatch_get_specific(&QueueIdentityKey)) {
dispatch_sync(self.dbConcurrentQueue, ^{
block();
});
}else{
block();
}
}
@end
- 数据库的增删改用asyncBarrierDBBlock;查用syncDBBlock
- 无论上层怎么嵌套调用,不会出现死锁。