从自定义串行队列

时间:2017-07-21 09:09:37

标签: ios multithreading concurrency grand-central-dispatch

我遇到了一个与iOS中的队列死锁相关的非常有趣的问题。有什么方法可以避免这种情况吗?

考虑一下:

  1. 创建自定义序列队列。
  2. 异步调度此串行队列上的某些任务(#1)。
  3. 此异步任务(#1)将某个任务(#2)调度到主队列同步。
  4. 主队列将一些任务(#3)调度到串行队列同步。
  5. 结果 - DeadLock
  6. 以下是此示例代码。

    • 由于self.opQueue是一个串行队列,任务#3将在任务#1完成之前不会启动。
    • 由于任务#1正在调用主队列同步,因此在主队列完成任务#2之前它永远不会完成。
    • 由于主队列正在等待opQueue完成任务#3,并且opQueue正在等待主队列完成任务#2,因此存在死锁。

      #import "ViewController.h"
      
      @interface ViewController ()
      @property(nonatomic,strong) dispatch_queue_t opQueue;
      @end
      
      @implementation ViewController
      
      - (void)viewDidLoad {
        [super viewDidLoad];
      
        dispatch_queue_t queue =  dispatch_queue_create("com.tarun.sqqueue",
                                                     DISPATCH_QUEUE_SERIAL);
        self.opQueue = queue;
      
        [self performOperations];
      }
      
      
      /// 1. Dispatch on serial queue async.
      /// 2. This async task on serial queue dispatchs some task onto 
      ///    main queue sync.
      /// 3. Main queue dispatched some task onto serial queue sync.
      /// 4. Result - DeadLock
      - (void)performOperations {
      
        /// task#1: Dispatch task on the serial Queue Asynchronously.
        /// So this is not blocking.
        dispatch_async(self.opQueue, ^{
          for (int i = 1; i<=100; i++) {
            NSLog(@"%d. Async on Serial Queue from Main Queue.",i);
          }
      
          /// task#2: Dispatching task on main queue synchronously from serial
          /// queue.So this queue will wait till main queue executes this task.(Blocking)
          dispatch_sync(dispatch_get_main_queue(), ^{
            for (int i = 1; i<=100; i++) {
              NSLog(@"%d. Sync on main queue from Serial Queue.",i);
            }
          });
        });
      
        /// task#3: Dispatching task on swrial queue synchronously from main
        /// queue.So main queue will wait till serial queue executes this task. (Blocking)
        dispatch_sync(self.opQueue, ^{
          for (int i = 1; i<=100; i++) {
            NSLog(@"%d. Sync on Serial Queue From Main Queue.",i);
          }
        });
      
        /// Since self.opQueue is a serial queue, task#3 will not start till task#1 completes.
        /// Since task#1 is calling main queue sync, 
        /// so it will never complete till main queue completes task#2.
        /// Since main queue is waiting for opQueue to finish task#3, and opQueue is waiting for main queue to finish task#2 there is a deadlock.
      
        NSLog(@"Back to main queue");
      }
      
      
      @end
      

1 个答案:

答案 0 :(得分:1)

来自Apple

  

重要提示:您永远不应该调用dispatch_sync或dispatch_sync_f   从您在同一队列中执行的任务中运行   计划传递给函数。这对于特别重要   串行队列,保证死锁,但也应该是   避免并发队列。

保证死锁。避免这种情况的唯一方法是不以这种方式实现它。