【问题标题】:What GCD queue, main or not, am I running on?我在哪个 GCD 队列上运行,无论是否是主队列?
【发布时间】:2011-02-09 08:25:35
【问题描述】:

我正在尝试编写一些线程安全的方法,所以我正在使用:

...
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_sync(main,^{
  [self doSomethingInTheForeground];
});
...

但是如果我在不必要的主线程上,我可以跳过所有那些调度调用,所以我想知道我目前在哪个线程上。我怎么知道?

或者,也许这样做并没有什么不同(在性能上)?

这样比较可以吗?

if (dispatch_get_main_queue() == dispatch_get_current_queue()){...}

【问题讨论】:

  • 使用提到的一种技术不仅可以减少开销,而且如果您对正在运行的同一队列执行 dispatch_sync,您将死锁。 dispatch_async 没问题。
  • 如果您需要更通用的解决方案来防止 libdispatch 中的死锁,请查看这些帮助程序。他们覆盖 main_queue,只是要小心。 gist.github.com/1205760
  • 这个问题很好,但是示例代码显示了死锁模式。也许修改一下比较好。

标签: objective-c multithreading cocoa grand-central-dispatch


【解决方案1】:

更新答案

Apple 文档已更改,现在说“当从提交块的上下文之外调用时,如果调用是从主线程执行的,则此函数返回主队列。如果调用是从任何其他线程进行的,此函数返回默认的并发队列。”所以检查dispatch_get_main_queue() == dispatch_get_current_queue() 应该可以。

原答案

使用dispatch_get_main_queue() == dispatch_get_current_queue() 将不起作用。 dispatch_get_current_queue 的文档说“当在提交块的上下文之外调用时,此函数返回默认并发队列”。默认并发队列不是主队列。

[NSThread isMainThread] 应该可以满足您的需求。请注意,[NSThread isMainThread] 可以适用于除主队列以外的队列,例如,当从主线程调用 dispatch_sync 时。

【讨论】:

  • 实际上,它说“当从提交块的上下文之外调用时,此函数如果调用是从主线程执行的,则返回主队列。如果调用是从任何其他线程进行的,此函数返回默认的并发队列。”
  • 非常感谢您的更新。我认为这是我们都期望它的工作方式:)
  • 看起来dispatch_get_current_queue() 现在已被弃用,有什么替代方案?
  • 你应该使用 dispatch_get_specific。请参阅stackoverflow.com/a/15725847/412916 以及 cmets 中 Apple 论坛的链接。
【解决方案2】:

随着dispatch_get_current_queue()的折旧,相当于(dispatch_get_main_queue() == dispatch_get_current_queue())

现在是通过队列标签比较是:

(dispatch_queue_get_label(dispatch_get_main_queue()) == dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))

【讨论】:

  • 请注意 dispatch_queue_get_label() 返回一个字符 *。检查两个调用返回的指针值是否足够? strcmp() 调用是多余的吗?
【解决方案3】:

更新答案:

dispatch_get_current_queue() 现已弃用。

原答案:

在 OS-X 10.8 上,在头文件 (queue.h) 中,在 dispatch_get_current_queue() 函数上方的注释中是这样写的:

当在主线程调用 dispatch_get_current_queue() 时,它可能 或者可能不会返回与 dispatch_get_main_queue() 相同的值。 比较两者不是测试代码是否正在执行的有效方法 在主线程上。

我发现是因为

assert(dispatch_get_current_queue() == dispatch_get_main_queue());

在我在 iOS 上运行良好但在 OS-X 上失败的代码中。

【讨论】:

    【解决方案4】:

    iOS 的新功能:10.0+ 和 macOS:10.12+(tvOS:10.0+,watchOS:3.0+)。

    斯威夫特 4+:

    检查是否在主队列上运行:

    if (RunLoop.current == RunLoop.main) {
        print("On main queue")
    }
       
    

    断言是否在主队列上运行:

    dispatchPrecondition(condition: .onQueue(.main))
    // Code running on main queue
     
    

    断言如果在主队列上运行:

    dispatchPrecondition(condition: .notOnQueue(.main))
    // Code NOT running on main queue
    

    ObjC:检查是否在主队列上运行:

    if ([[NSRunLoop currentRunLoop] isEqual: [NSRunLoop mainRunLoop]]) {
        NSLog(@"Running on main");
    } else {
        NSLog(@"Not running on main");
    }
       
    

    断言是否在主队列上运行:

    dispatch_assert_queue(dispatch_get_main_queue());
       
    

    断言如果在主队列上运行:

    dispatch_assert_queue_not(dispatch_get_main_queue());
    

    注意:mainThread != mainQueue 主队列总是在主队列上运行 线程,但一个队列可能正在主线程上运行而没有 主队列。所以,不要混合使用线程和队列测试!

    【讨论】:

      【解决方案5】:

      如果你在 Objective-C 中,并且希望在主线程上同步发生一些事情,使用它会不会更简单

      [self performSelectorOnMainThread: @selector(doSomethingInTheForeground) 
                             withObject: nil 
                          waitUntilDone: YES];
      

      这样做的好处是,如果你已经在主线程上,没关系,消息以正常方式发送。

      【讨论】:

        【解决方案6】:

        使用dispatch_get_current_queue() 是不安全的,除非你没有调试。写得很清楚in the dispatch_queue man page

        注意事项

        代码无法对dispatch_get_current_queue() 返回的队列做出任何假设。返回的队列可能有任意的策略,这些策略可能会让试图调度队列工作的代码感到惊讶。策略列表包括但不限于队列宽度(即串行与并发)、调度优先级、安全凭证或文件系统配置。因此,dispatch_get_current_queue() 只能用于身份测试或调试。

        更好的事情(可能更复杂)是同步后台线程:

        dispatch_sync(backgroundqueue,^{
          [self doSomethingInTheBackground];
        });
        

        也许我完全错了,但这就是我的建议。

        【讨论】:

        • 明确声明“用于身份测试”没问题。这就是最初的问题所暗示的(尽管这不会像我在回答中提到的那样起作用)。
        【解决方案7】:

        其实可以用的。

        文档说

        当从提交块的上下文之外调用时,这个 如果调用是从主队列执行的,函数返回主队列 线程。如果调用是从任何其他线程进行的,则此函数 返回默认的并发队列。

        【讨论】:

          【解决方案8】:

          请注意,主队列与主线程不同。当在主线程的队列上使用 dispatch_sync() 时,非主队列很容易在主线程上运行。更罕见的是,在使用 dispatch_main() 代替 NSRunLoops 的命令行工具中(即 Cocoa/iOS 应用程序除外),主队列可能在主线程以外的其他地方执行。

          如果您正在处理需要主队列而不仅仅是主线程的代码(例如,期望从 queue_get_specific 在主队列上设置的值的代码,据传闻 VectorKit/MapKit 会这样做),最好显式检查主队列而不是主线程。

          一个显式检查主队列的选项:

          BOOL MyIsMainQueue(void)
          {
              static char MAIN_IND_KEY;
              static char MAIN_IND_VAL;
              static dispatch_once_t onceToken;
              dispatch_once(&onceToken, ^{
                  dispatch_queue_set_specific(dispatch_get_main_queue(), &MAIN_IND_KEY, &MAIN_IND_VAL, NULL);
              });
          
              return dispatch_get_specific(&MAIN_IND_KEY) == &MAIN_IND_VAL;
          }
          

          使用 DISPATCH_CURRENT_QUEUE_LABEL 的答案也应该有效,并且可能会更好,尽管在 MacOS 10.9 和 iOS7 之前定义 DISPATCH_CURRENT_QUEUE_LABEL 时可能无效(即崩溃)。

          【讨论】:

            【解决方案9】:

            dispatch_get_current_queue 自 iOS 6.0 起已弃用。看起来[NSThread isMainThread] 是唯一的出路。

            【讨论】:

            • 我在苹果和第三方的许多教程和文档中看到了 dispatch_get_main_queue 的用法。它似乎是一个非常广泛使用的功能。我因此挑战它一般被弃用。能给个参考吗?有直接替代策略吗?
            • 你是对的,我剪切/粘贴函数的名称是错误的。不推荐使用的是 dispatch_get_current_queue 。很抱歉造成混乱。然而,结论仍然成立。 [NSThread isMainThread] 是唯一的方法(尽管它并不总是意味着你在主队列中)。
            • 啊哈,对我来说也是新的,但是我同意。 :)
            猜你喜欢
            • 1970-01-01
            • 2021-11-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-10-11
            • 1970-01-01
            • 2012-09-30
            • 1970-01-01
            相关资源
            最近更新 更多