iOS

iOS 解决同步任务提交当前队列死锁问题

2021-09-01  本文已影响0人  某非著名程序员

场景

同步提交到主队列

- (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
  1. 数据库的增删改用asyncBarrierDBBlock;查用syncDBBlock
  2. 无论上层怎么嵌套调用,不会出现死锁。
上一篇 下一篇

猜你喜欢

热点阅读