串行队列的死锁

Posted AC自动机

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了串行队列的死锁相关的知识,希望对你有一定的参考价值。

问题

这个死锁是会发生在 dispatch_sync 这个GCD接口调用上。

Summary

Submits a block object for execution on a dispatch queue and waits until that block completes.

1void dispatch_sync(dispatch_queue_t queuedispatch_block_t block);

Discussion

Submits a block to a dispatch queue for synchronous execution. Unlike dispatch_async, this function does not return until the block has finished. Calling this function and targeting the current queue results in deadlock.
Unlike with dispatch_async, no retain is performed on the target queue. Because calls to this function are synchronous, it "borrows" the reference of the caller. Moreover, no Block_copy is performed on the block.
As an optimization, this function invokes the block on the current thread when possible.

正如 接口文档中所说的那样: Calling this function and targeting the current queue results in deadlock 在当前线程队列中使用本接口是会导致死锁的。

这个死锁情况仅限在 DISPATCH_QUEUE_SERIAL的线程队列中。串行队列同步调用会死锁等待。

如何解决

使用串行队列可以避免加锁操作,像一些著名的三方库GCDAsyncSocket,FMDB都有这种使用场景:

GCDAsyncSocket

 1if (sq)
2{
3    NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
4                @"The given socketQueue parameter must not be a concurrent queue.");
5    NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
6                @"The given socketQueue parameter must not be a concurrent queue.");
7    NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
8                @"The given socketQueue parameter must not be a concurrent queue.");
9
10    socketQueue = sq;
11    #if !OS_OBJECT_USE_OBJC
12    dispatch_retain(sq);
13    #endif
14else {
15    socketQueue = dispatch_queue_create([GCDAsyncSocketQueueName UTF8String], NULL);
16}
1IsOnSocketQueueOrTargetQueueKey = &IsOnSocketQueueOrTargetQueueKey;        
2void *nonNullUnusedPointer = (__bridge void *)self;
3dispatch_queue_set_specific(socketQueue, IsOnSocketQueueOrTargetQueueKey, nonNullUnusedPointer, NULL);

再看一下他的sync逻辑是如何控制:

1if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) {
2    [self closeWithError:nil];
3else {
4    dispatch_sync(socketQueue, ^{
5        [self closeWithError:nil];
6    });
7}

用意就很明确了,在sync可能会导致死锁的情况下,先判断一下是否已经处于当前队列中。

还有一个不推荐的做法来自GPUImage

 1#if !OS_OBJECT_USE_OBJC
2#pragma clang diagnostic push
3#pragma clang diagnostic ignored "-Wdeprecated-declarations"
4    if (dispatch_get_current_queue() == videoProcessingQueue)
5#pragma clang diagnostic pop
6#else
7    if (dispatch_get_specific([GPUImageContext contextKey]))
8#endif
9    {
10        block();
11    }else {
12        dispatch_sync(videoProcessingQueue, block);
13    }

这里他还使用dispatch_get_current_queue()API, 但苹果的文档中已经标注为废弃API了。

This function is deprecated and will be removed in a future release.
不会在未来的版本中保证该API是否还会存在。


以上是关于串行队列的死锁的主要内容,如果未能解决你的问题,请参考以下文章

ios多线程同步异步、串行并行队列、死锁

串行队列上的核心数据堆栈导致死锁

说说GCD中的死锁

swift/OC中的死锁问题

多线程处理怎么使用绿联

iOS--关于GCD的一些疑惑