【问题标题】:Dispatch rule of concurrent queue using GCD when sleep in one thread单线程休眠时使用GCD的并发队列调度规则
【发布时间】:2016-07-13 21:33:03
【问题描述】:

测试代码如下:

dispatch_queue_t queue = dispatch_queue_create("sc", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
    [NSThread sleepForTimeInterval:10];
    NSLog(@"10s --- %@", [NSThread currentThread]);
});

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), queue, ^{
    NSLog(@"First: 5s --- %@", [NSThread currentThread]);
});

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), queue, ^{
    NSLog(@"Second: 5s --- %@", [NSThread currentThread]);
});

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), queue, ^{
    NSLog(@"Third: 5s --- %@", [NSThread currentThread]);
});

这里我创建了一个并发队列,并使用异步方式休眠一个线程。

但是,输出是:

2016-03-26 12:17:57.164 TestPlayground[28188:551287] 10s --- <NSThread: 0x7fa080511730>{number = 2, name = (null)}
2016-03-26 12:17:57.165 TestPlayground[28188:551287] First: 5s --- <NSThread: 0x7fa080511730>{number = 2, name = (null)}
2016-03-26 12:17:57.166 TestPlayground[28188:551307] Second: 5s --- <NSThread: 0x7fa080704a80>{number = 3, name = (null)}
2016-03-26 12:17:57.166 TestPlayground[28188:551301] Third: 5s --- <NSThread: 0x7fa080511530>{number = 4, name = (null)}

GCD 似乎阻塞了第一个线程 10 秒,然后开始为并发工作创建新线程。

但是如果我只是用全局并发队列替换自己创建的队列并且不更改以下作业

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// ... same as before

输出更合理

2016-03-26 12:23:29.496 TestPlayground[28320:558467] First: 5s --- <NSThread: 0x7ff473c08b80>{number = 4, name = (null)}
2016-03-26 12:23:29.496 TestPlayground[28320:558483] Second: 5s --- <NSThread: 0x7ff473f18c50>{number = 2, name = (null)}
2016-03-26 12:23:29.496 TestPlayground[28320:558471] Third: 5s --- <NSThread: 0x7ff473d0a7c0>{number = 3, name = (null)}
2016-03-26 12:23:34.030 TestPlayground[28320:558456] 10s --- <NSThread: 0x7ff473e218e0>{number = 5, name = (null)}

GCD 中此类作业的调度规则是什么?以及为什么自创并发队列与全局队列不同?


如果我在睡眠作业之前添加新作业

dispatch_queue_t queue = dispatch_queue_create("sc", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{ NSLog(@"start --- %@", [NSThread currentThread]); });

dispatch_async(queue, ^{
    [NSThread sleepForTimeInterval:10];
    NSLog(@"10s --- %@", [NSThread currentThread]);
});
// ... same as before

输出是

2016-03-26 12:44:25.728 TestPlayground[28616:571502] start --- <NSThread: 0x7fb7c1604250>{number = 2, name = (null)}
2016-03-26 12:44:35.733 TestPlayground[28616:571511] 10s --- <NSThread: 0x7fb7c1422e40>{number = 3, name = (null)}
2016-03-26 12:44:35.734 TestPlayground[28616:571517] First: 5s --- <NSThread: 0x7fb7c16032d0>{number = 4, name = (null)}
2016-03-26 12:44:35.734 TestPlayground[28616:571511] Second: 5s --- <NSThread: 0x7fb7c1422e40>{number = 3, name = (null)}
2016-03-26 12:44:35.734 TestPlayground[28616:571552] Third: 5s --- <NSThread: 0x7fb7c1608950>{number = 5, name = (null)}

【问题讨论】:

  • 作为测试,在调用包含睡眠的dispatch_async 之前调用dispatch_async(queue, ^{ NSLog(@"start --- %@", [NSThread currentThread]); });。有什么影响?
  • @rmaddy 我已经用输出更新了帖子。看来它只是使用新线程来完成这项工作。
  • @xi.lin 是的——这很奇怪。我会向调度团队询问这种行为,如果有机会,我很好奇,我会自己提取源代码并调试它。

标签: objective-c multithreading concurrency grand-central-dispatch thread-sleep


【解决方案1】:

考虑以下输出:

00:53:38.852 asdfasdfasdf[11650:2114104] start
00:53:38.854 asdfasdfasdf[11650:2114104] main <NSThread: 0x100206110>{number = 1, name = main}
00:53:38.854 asdfasdfasdf[11650:2114126] Enter sleep block
00:53:38.854 asdfasdfasdf[11650:2114127] bare async
00:53:48.858 asdfasdfasdf[11650:2114126] Sleep block done (10 seconds).
00:53:48.859 asdfasdfasdf[11650:2114126] dispatch_after() 5 --- <NSThread: 0x10020aa00>{number = 2, name = (null)}

因此,休眠线程不会阻塞异步队列块的调用(即使抛出了线程休眠),但会阻塞 dispatch_after() 的执行。

这让我很困惑。 rdar://25373048

    dispatch_queue_t queue = dispatch_queue_create("sc", DISPATCH_QUEUE_CONCURRENT);

    NSLog(@"start");
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"main %@", [NSThread currentThread]);

        dispatch_async(queue, ^{
            NSLog(@"Enter sleep block");
            [NSThread sleepForTimeInterval:10];
            NSLog(@"Sleep block done (10 seconds).");
        });

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), queue, ^{
            NSLog(@"dispatch_after() 5 --- %@", [NSThread currentThread]);
        });

        dispatch_async(queue, ^{
            NSLog(@"bare async");
        });
    });

    [[NSRunLoop currentRunLoop] run];

【讨论】:

  • 是的,dispatch_after 似乎与 dispatch_async 不同。而且似乎全局并发队列有一些特殊的策略。
  • @xi.lin 这是调度中的一个错误,将在未来的某个版本中修复(不知道什么时候)。现在,我建议在全局队列中使用 dispatch_after,然后在目标队列中使用 dispatch_async,如果你真的需要这种模式的话。
  • 所以你的雷达现在已经被开发团队确认了?感谢您的信息。昨晚尝试阅读了libdispatch的源码,但还没找到原因。
  • @xi.lin 是 - 已知问题。我能够使用 lib dispatch 的调试构建来构建和运行,但是内省的东西妨碍了我实际上无法通过 dispatch 进行调试。看看它陷入的状态,交互有点微妙。
  • 感谢您的帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-16
  • 2012-12-11
  • 2013-10-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多