美文网首页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 解决同步任务提交当前队列死锁问题

    场景 同步提交到主队列 死锁,崩溃。 原因: 主队列在执行viewDidLoad任务,又同步执行block任务;v...

  • GCD解析

    死锁(Deadlock) 在串行队列中,当前队列的调度块内(包含嵌套)调用当前队列的同步任务会死锁异步调度块要等自...

  • 多线程相关

    1. GCD 1. 同步/异步 串行/并发 死锁原因队列引起的循环等待主队列中所提交的任务无论是同步方式还是异步...

  • GCD 死锁

    死锁 什么是死锁?在当前队列中, 追加一个任务; 如果使用同步函数, 则在当前线程中执行源代码, 与等待执行追加的...

  • iOS线程死锁

    结论:使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列(产生死锁) 首先你要理解同步和异步执行的概念...

  • iOS 多线程面试题(死锁)

    死锁 死锁就是队列引起的循环等待 1、一个比较常见的死锁例子:主队列同步 在主线程中运用主队列同步,也就是把任务放...

  • GCD总结1

    产生死锁原因:使用sync函数往当前串行队列里面添加任务,会卡住当前串行队列(产生死锁)。

  • 死锁gcd

    串行队列中添加同步会造成死锁,互相等待同步任务执行完才能执行下一个 全局的并行队列不会死锁

  • iOS---多线程面试问题

    死锁的原因:队列引起的循环等待 逻辑:在主队列中提交了viewDidLoad任务,然后提交 block任务,这2个...

  • iOS底层原理——浅谈多线程

    iOS-浅谈多线程 一、造成线程堵塞的条件: 1、当前串行队列有任务。2、在这个任务中使用同步函数给当前串行队列添...

网友评论

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

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