串行队列的死锁
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 queue, dispatch_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
14} else {
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];
3} else {
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是否还会存在。
以上是关于串行队列的死锁的主要内容,如果未能解决你的问题,请参考以下文章