Home > Net >  Does `DispatchQueue.global().async` make new global queue?
Does `DispatchQueue.global().async` make new global queue?

Time:12-18

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")
}

If the global queue is not stored in a variable, the order of A, B, C, D is different each time.
When the global queue is stored in a variable, A, B, C, and D are called in sequence always(*in a playground).

I wonder why the code execution results above and below are different.
Are there multiple global queues?

CodePudding user response:

I ran your code and got:

B
B
A
D
A
C
C
D

So all 8 print statements are interleaved with one another, not entirely randomly but with no particular pattern either. Earlier print statements tend to execute a bit earlier than later ones, but basically it's unpredictable — it's asynchronous.

CodePudding user response:

It's probably just a coincidence that they print in the correct order. The docs for DispatchQueue.global() say.

Tasks submitted to the returned queue are scheduled concurrently with respect to one another. https://developer.apple.com/documentation/dispatch/dispatchqueue/2300077-global#

Since it says they're scheduled concurrently (as opposed to serially), it shouldnt be expected that they'll always print in the same order.

The async tasks also are not really doing anything. I'd suspect there isn't enough variability between them to cause a different order to be printed.

DispatchQueue.global() looks like it always returns the same instance based on the memory addresses I'm seeing in a playground. It may be doing something extra under the hood that may be introducing a little bit a variability in the execution time of each async task.

Swapping the order of the of the two tests in your code also produced a different output for me, confirming that the order shouldn't be relied on.

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")
}

CodePudding user response:

let a = DispatchQueue.global()

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

a.async {
    print("1")
}
a.async {
    print("2")
}
a.async {
    print("3")
}
a.async {
    print("4")
}

A
1 // usually first or second
C
D
B
2
3
4

I've tried a variety of configurations but this one struck me the most. If you capture the queue in a property before submitting anything to any queue, the first task in the captured queue almost always executes first or second among all tasks. I've also observed that tasks in the inline queues almost always execute in random while the other tasks almost always execute in serial. Putting all of this together, it appears that capturing a global queue in a property gives that queue a slightly higher priority than a queue that is accessed inline. All of these tests were done in a playground so none of this might mean anything in production environments.

CodePudding user response:

Other answers have discussed the order in which blocks are executed, but I want to directly address your question: “Does DispatchQueue.global().async make a new global queue?”

It does not.

We can check empirically by printing the ObjectIdentifier of the queue:

import Dispatch

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

It prints the same ObjectIdentifier twice, so both calls to DispatchQueue.global() return the same object.

We can also answer the question by looking at the source code. DispatchQueue.global() is a Swift wrapper for the C function dispatch_get_global_queue. The source is 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);
}

It calls _dispatch_get_root_queue. The source for that is 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];
}

It's just an array lookup, so it returns the same value every time, unless the contents of _dispatch_root_queues changes—which it doesn't.

  • Related