【问题标题】:Does `DispatchQueue.global().async` make new global queue?`DispatchQueue.global().async` 是否会创建新的全局队列?
【发布时间】:2021-12-17 22:50:59
【问题描述】:
DispatchQueue.global().async {
    print("A")
}
DispatchQueue.global().async {
    print("B")
}
DispatchQueue.global().async {
    print("C")
}
DispatchQueue.global().async {
    print("D")
}

let a = DispatchQueue.global()

a.async {
    print("A")
}
a.async {
    print("B")
}
a.async {
    print("C")
}
a.async {
    print("D")
}

如果全局队列没有存储在变量中,那么A、B、C、D的顺序每次都不一样。
当全局队列存储在变量中时,总是按顺序调用 A、B、C 和 D(*在游乐场中)。

我想知道为什么上面和下面的代码执行结果不同。
是否有多个全局队列?

【问题讨论】:

  • 永远不要在操场上测试任何涉及线程或内存管理的东西。

标签: swift grand-central-dispatch dispatch dispatch-queue


【解决方案1】:

其他答案已经讨论了块的执行顺序,但我想直接解决您的问题:“DispatchQueue.global().async 是否创建了一个新的全局队列?”

没有。

我们可以通过打印队列的ObjectIdentifier来凭经验检查:

import Dispatch

let q = DispatchQueue.global()
print(ObjectIdentifier(DispatchQueue.global()))
print(ObjectIdentifier(q))

它两次打印相同的ObjectIdentifier,因此对DispatchQueue.global() 的两次调用都返回相同的对象。

我们也可以通过查看源代码来回答这个问题。 DispatchQueue.global() 是 C 函数 dispatch_get_global_queue 的 Swift 包装器。来源是here

dispatch_queue_global_t
dispatch_get_global_queue(intptr_t priority, uintptr_t flags)
{
    dispatch_assert(countof(_dispatch_root_queues) ==
            DISPATCH_ROOT_QUEUE_COUNT);


    if (flags & ~(unsigned long)DISPATCH_QUEUE_OVERCOMMIT) {
        return DISPATCH_BAD_INPUT;
    }
    dispatch_qos_t qos = _dispatch_qos_from_queue_priority(priority);
#if !HAVE_PTHREAD_WORKQUEUE_QOS
    if (qos == QOS_CLASS_MAINTENANCE) {
        qos = DISPATCH_QOS_BACKGROUND;
    } else if (qos == QOS_CLASS_USER_INTERACTIVE) {
        qos = DISPATCH_QOS_USER_INITIATED;
    }
#endif
    if (qos == DISPATCH_QOS_UNSPECIFIED) {
        return DISPATCH_BAD_INPUT;
    }
    return _dispatch_get_root_queue(qos, flags & DISPATCH_QUEUE_OVERCOMMIT);
}

它调用_dispatch_get_root_queue。来源是here

DISPATCH_ALWAYS_INLINE DISPATCH_CONST
static inline dispatch_queue_global_t
_dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
{
    if (unlikely(qos < DISPATCH_QOS_MIN || qos > DISPATCH_QOS_MAX)) {
        DISPATCH_CLIENT_CRASH(qos, "Corrupted priority");
    }
    return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}

这只是一个数组查找,所以它每次都返回相同的值,除非 _dispatch_root_queues 的内容发生变化——它不会。

【讨论】:

    【解决方案2】:

    它们以正确的顺序打印可能只是一个巧合。 DispatchQueue.global() 的文档说。

    提交到返回队列的任务是相对于彼此同时调度的。 https://developer.apple.com/documentation/dispatch/dispatchqueue/2300077-global#

    因为它说它们是同时安排的(而不是串行),所以不应该期望它们总是以相同的顺序打印。

    异步任务也没有真正做任何事情。我怀疑它们之间没有足够的可变性导致打印不同的订单。

    DispatchQueue.global() 看起来总是根据我在操场上看到的内存地址返回相同的实例。它可能在幕后做了一些额外的事情,可能会在每个异步任务的执行时间上引入一些可变性。

    在您的代码中交换两个测试的顺序也为我产生了不同的输出,确认不应该依赖该顺序。

    A2
    C2
    D2
    B2
    A1
    B1
    C1
    D1
    
    let a = DispatchQueue.global()
    
    a.async {
        print("A2")
    }
    a.async {
        print("B2")
    }
    a.async {
        print("C2")
    }
    a.async {
        print("D2")
    }
    
    DispatchQueue.global().async {
        print("A1")
    }
    DispatchQueue.global().async {
        print("B1")
    }
    DispatchQueue.global().async {
        print("C1")
    }
    DispatchQueue.global().async {
        print("D1")
    }
    

    【讨论】:

    • 在 Playground 中执行时,它们按顺序出现,但在 CLT 中执行时,它们是无序的。你能解释一下两者之间的区别吗?
    【解决方案3】:

    我运行了你的代码并得到:

    B
    B
    A
    D
    A
    C
    C
    D
    

    所以所有 8 个打印语句都相互交错,不是完全随机的,但也没有特定的模式。早期的 print 语句往往比后面的更早执行,但基本上它是不可预测的——它是异步的。

    【讨论】:

      猜你喜欢
      • 2018-02-05
      • 2021-11-13
      • 2018-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-19
      • 2012-06-21
      • 1970-01-01
      相关资源
      最近更新 更多