“阻塞”主线程(dispatch_get_main_queue())和(或不)定期运行 currentRunLoop - 有啥区别?

Posted

技术标签:

【中文标题】“阻塞”主线程(dispatch_get_main_queue())和(或不)定期运行 currentRunLoop - 有啥区别?【英文标题】:"Block" main thread (dispatch_get_main_queue()) and (or not) run currentRunLoop periodically - what is the difference?“阻塞”主线程(dispatch_get_main_queue())和(或不)定期运行 currentRunLoop - 有什么区别? 【发布时间】:2012-11-29 06:34:28 【问题描述】:

我有以下代码:

- (void)test_with_running_runLoop 
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    NSTimeInterval checkEveryInterval = 0.05;

    NSLog(@"Is main queue? : %d", dispatch_get_current_queue() == dispatch_get_main_queue());

    dispatch_async(dispatch_get_main_queue(), ^
        sleep(1);
        NSLog(@"I will reach here, because currentRunLoop is run");
        dispatch_semaphore_signal(semaphore);
    );

    while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW))
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:checkEveryInterval]];

    NSLog(@"I will see this, after dispatch_semaphore_signal is called");


- (void)test_without_running_runLoop 
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    NSLog(@"Is main queue? : %d", dispatch_get_current_queue() == dispatch_get_main_queue());

    dispatch_async(dispatch_get_main_queue(), ^
        sleep(1);
        NSLog(@"I will not reach here, because currentRunLoop is not run");
        dispatch_semaphore_signal(semaphore);
    );

    NSLog(@"I will just hang here...");
    while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW));

    NSLog(@"I will see this, after dispatch_semaphore_signal is called");

产生以下内容:

Starting CurrentTests/test_with_running_runLoop
2012-11-29 08:14:29.781 Tests[31139:1a603] Is main queue? : 1
2012-11-29 08:14:30.784 Tests[31139:1a603] I will reach here, because currentRunLoop is run
2012-11-29 08:14:30.791 Tests[31139:1a603] I will see this, after dispatch_semaphore_signal is called
OK (1.011s)

Starting CurrentTests/test_without_running_runLoop
2012-11-29 08:14:30.792 Tests[31139:1a603] Is main queue? : 1
2012-11-29 08:14:30.797 Tests[31139:1a603] I will just hang here...

我的问题彼此相关:

    如果我理解正确的话,主队列 (dispatch_get_main_queue()) 是一个串行队列。我用dispatch_semaphore_wait阻塞了主队列/主线程,那么为什么我在第一个测试用例中看到“我会到达这里,因为currentRunLoop正在运行”(我对第二种情况没问题-以我的理解,它确实,它应该做什么)?

    串行队列如何在当前执行任务被阻塞的情况下,在当前任务解锁之前调度下一个任务(哦,这个神秘的 runLoop:beforeDate:)?

我想听听详细全面对此的回答,因为非常非常多的事情(这里也是如此)取决于这个问题!

更新:除了公认的答案,这个 SO 主题对这个问题有很好的答案:Pattern for unit testing async queue that calls main queue on completion

【问题讨论】:

【参考方案1】:

因为主线程上的默认 runloop 具有特殊行为,即在运行时,它也会为主调度队列进行处理。在这种情况下,您实际上并没有阻塞,因为您告诉dispatch_semaphore_wait 立即超时,它正在这样做(返回非零,在您的if 中计算为真)-因此您运行了您的 while 循环,其中你驱动当前的运行循环,因此你的排队块被执行。

但我的回答很笼统,因为我不确定你对哪一部分感到惊讶。

【讨论】:

"因为主线程上的默认 runloop 具有特殊行为,当运行时,它也会为主调度队列处理。" 1)我在哪里可以更多地了解这种“特定”行为——你能详细描述一下吗? 2) 这种行为是否仅特定于主队列/线程或任何串行队列/线程,而不是主线程? 3)我的第二个问题:你能描述一下它是如何应用于主队列的,应用于任何串行队列吗?我确认第二个问题足以表达我的惊讶。 它只适用于主线程/调度队列。请参阅docs。

以上是关于“阻塞”主线程(dispatch_get_main_queue())和(或不)定期运行 currentRunLoop - 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

java界面子线程界面阻塞了主线程界面怎么解决?

Android主线程阻塞WebView线程

在没有线程阻塞的情况下回调主线程(Java)

C++多线程阻塞主线程

.NET - 阻塞主线程,直到有任何可用线程

当主线程显然未阻塞时,QProgressDialog 冻结