`DispatchQueue.global().async` 是不是会创建新的全局队列?

Posted

技术标签:

【中文标题】`DispatchQueue.global().async` 是不是会创建新的全局队列?【英文标题】: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(*在游乐场中)。 我想知道为什么上面和下面的代码执行结果不同。 是否有多个全局队列?

【问题讨论】:

永远不要在操场上测试任何涉及线程或内存管理的东西。 【参考方案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 语句往往比后面的更早执行,但基本上它是不可预测的——它是异步的。

【讨论】:

以上是关于`DispatchQueue.global().async` 是不是会创建新的全局队列?的主要内容,如果未能解决你的问题,请参考以下文章

Swift 5:我无法让我的 UITableView 及时更新(同时使用 `DispatchQueue.global().sync` 和 `DispatchQueue.main.async`

Swift:使用 DispatchQueue.global (qos: .userInitiated) .asyncAfter 重复返回的结果

Swift:使用DispatchQueue.global(qos:.userInitiated).asyncAfter]重复返回的结果

GCD 定时器

swift GCD的简单使用

GCD 主线程崩溃问题(需要解释)?