美文网首页iOS
iOS 解决同步任务提交当前队列死锁问题

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

作者: 某非著名程序员 | 来源:发表于2021-09-01 19:05 被阅读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. 无论上层怎么嵌套调用,不会出现死锁。

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/foaxwltx.html