dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ NSLog(@"sync1:%@", [NSThread currentThread]); });
输出 sync1:<NSThread: 0x7fa731f06030>{number = 1, name = main}
这段代码是在主线程执行的, 在调试这段代码前,一直以为会死锁。我的理解是 dispatch_sync 同步操作会阻塞当前线程即主线程,同时队列将这个任务放到主线程执行(从输出看到),发生了双向阻塞为什么没有死锁。有请高手指证问题。
1 zwzmzd 2016-04-09 18:37:53 +08:00 via Android 无责任猜测, dispatch_sync 可以认为是“让出当前线程并等待完成” |
3 zwzmzd 2016-04-09 19:11:08 +08:00 参考操作系统中阻塞型调用的实现,例如 socket 中的 recv 。此类调用在没有数据的情况下,相当于将控制权交还给系统调度器,而不是占有线程继续忙等待。 你的代码可以理解为下面的步骤: 1.将 block 注册为一个任务,并加入主线程“待执行”队列 2.注册完毕后,将自己加入主线程“待执行”队列,并让出当前线程(主线程) 3.调度器从主线程“待执行”队列中调度出刚刚注册的 GCD ,于是 NSLog(@"sync1:%@", [NSThread currentThread]);得到了执行。 4.GCD 执行完毕后,让出当前线程(主线程) 5.调度器从主线程“待执行”队列取出外层的任务,发现其等待条件已经得到满足,继续允许执行 |
![]() | 4 Vernsu 2016-04-09 20:16:00 +08:00 你看,这样就死锁了。 dispatch_queue_t queue = dispatch_queue_create("serial1", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ dispatch_sync(queue, ^{ NSLog(@"sync1:%@", [NSThread currentThread]); }); NSLog(@"sync1:%@", [NSThread currentThread]); }); |
![]() | 5 Vernsu 2016-04-09 20:19:41 +08:00 没有死锁的原因是 block 块中的任务加入了你所创建的串行队列,而不是主队列。所以不存在违背 FIFO 原则的问题。 以上都是我猜的。 |
![]() | 6 zhangchioulin 2016-04-09 20:26:22 +08:00 mark 下班后到家了回复你 |
![]() | 7 LINAICAI 2016-04-09 21:20:35 +08:00 死锁是两个线程之间竞争资源,但都拿不到访问资源的锁时造成的 |
![]() | 8 Dean OP 自己查了下官方的文档 https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/index.html 其中得出 GCD 出现死锁的结论: dispatch_sync 的当前执行队列与提交 block 执行的目标队列相同时将造成死锁。此处 Block 所在的队列与 dispatch_sync 在队列不同分别是自定义队列与主队列,至于 Block 输出的线程是主线程应该是线程池中的线程重用。 |
![]() | 9 xi_lin 2016-04-09 23:29:52 +08:00 @Dean 推荐一个分析 libdispatch 的系列 http://blog.csdn.net/passerbysrs/article/details/18407959 这篇里提到 dispatch_sync 分发 block 到串行队列时会走到 queue.c 里的_dispatch_barrier_sync_f_slow 方法,里面会试图获取当前 thread 的 semaphore 。所以如果 dispatch_sync 执行时无法获取到信号量,就会一直死锁等下去了。 |
![]() | 10 Alchemistxxd 2016-04-10 00:41:41 +08:00 queue != current Q, 所以 dispatch_sync 只是阻塞 queue ,并不会死锁。死锁的一个典型例子是 dispatch_sync(dispatch_get_main_queue(), ^{ }); |
![]() | 11 codeisjobs 2016-04-10 00:50:14 +08:00 via iPhone 不管主队列还是其他队列,同步函数在手动创建的串行队列中是串行执行,不会发生死锁。只有同步函数在主队列中的时候,同时在主队列调用才会发生死锁。 |
![]() | 12 codeisjobs 2016-04-10 00:57:10 +08:00 via iPhone ![]() 主队列中开了一个普通的串行队列来执行同步函数,并不会影响主队列,这是两个不同的队列。你要是在主队列中用主队列执行同步函数,你等我,我等你,才会死锁。 |
![]() | 13 Loker 2016-04-10 21:19:25 +08:00 这样才会死锁 NSLog(@"Task 1"); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"Task 2"); }); NSLog(@"Task 3"); |
14 EdwardEan 2016-04-12 14:28:51 +08:00 不知道题主有没有读过 dispatch_sync 方法的这一段注释: As an optimization, dispatch_sync() invokes the block on the current thread when possible. 如果明白了请试着考虑下下面一段代码的输出结果: |
15 EdwardEan 2016-04-12 14:29:43 +08:00 接上一段回复内容: dispatch_sync(dispatch_get_global_queue(0, 0), ^{ [[NSOperationQueue mainQueue] addOperationWithBlock:^{ NSLog(@"First"); }]; [[NSOperationQueue currentQueue] addOperationWithBlock:^{ NSLog(@"Three"); }]; __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"MyNotif" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { NSLog(@"Receive Notif"); [[NSNotificationCenter defaultCenter] removeObserver:observer]; }]; [[NSOperationQueue currentQueue] addOperationWithBlock:^{ NSLog(@"Forth"); }]; [[NSNotificationCenter defaultCenter] postNotificationName:@"MyNotif" object:self]; [[NSOperationQueue currentQueue] addOperationWithBlock:^{ NSLog(@"Five"); }]; }); |
16 lee0100317 2016-04-13 17:29:05 +08:00 请注意区分 Thread 和 dispatch_queue , dispatch_sync 阻塞的是 dispatch_queue ,而不是 Thread , Thread 只是 dispatch_queue 完成调度的载体而已。 dispatch_queue 存在的意义就是希望可以不再涉及 Thread 的内容,以及彻底抛弃锁。 |
![]() | 17 Biscuits 2019-05-30 10:31:08 +08:00 @lee0100317 的确是这样, 他没搞清楚 dispatch_queue 和执行的 Thread 其实是分开的. 先回答为什么没有死锁问题 dispatch_sync 文档的 discussion 里面有这么一句 "Calling this function and targeting the current queue results in deadlock." 所以死锁问题有正确答案了. https://developer.apple.com/documentation/dispatch/1452870-dispatch_sync 然后解释为什么是主线程 还是的文档里面的 "As a performance optimization, this function executes blocks on the current thread whenever possible, with one obvious exception." 现在有 新建的 queue 和 main dispatch queue, 都是任务, Main_Thread 是(执行)资源. 在 Main_thread 调用 dispatch_sync, 把 block 任务加入 新 queue, 然后按照"this function executes blocks on the current thread whenever possible" 新 queue 拥有了主线程的执行资源, 进行执行, 然后返回继续 main dispatch queue . 所以 main dispatch queue 是持有了什么东西造成了死锁呢? |