2

These queues manage the tasks you provide to GCD and execute those tasks in FIFO order. This guarantees that first task added to the queue is the first task started in the queue, the second task added will be the second to start, and so on down the line. below code

let anotherQueue = DispatchQueue(label: "com.gcdTest.Queue", qos: .userInteractive)
anotherQueue.async {
    anotherQueue.async{
        anotherQueue.async{
            anotherQueue.async {
                print("task 6")
                for _ in 1...300 { }
            }
        }
        print("task 3")
        for _ in 301...600 {}
    }
    anotherQueue.async{
        anotherQueue.async{
            print("task 5")
            for _ in 700...900 {}
        }
        print("task 4")
        for _ in 5000...7000 {}
    }
    print("task 1")
    for _ in 9000...10000 {}
}
anotherQueue.async {
    print("task 2")
    for _ in 1...1000 {}
}

produces output

task 1
task 2
task 3
task 4
task 5
task 6

But when we run the same code in Concurrent it produces unpredictable output. ex:- change first line of code to below line

let anotherQueue = DispatchQueue(label: "com.gcdTest.Queue", qos: .userInteractive, attributes: .concurrent)

output

task 3
task 2
task 1
task 4
task 5
task 6

By definition it states Tasks in concurrent queues are guaranteed to start in the order they were added…and that’s about all you’re guaranteed!

So, expecting a similar output which is produced by serial queue(by default). (task1, task2, task3, task4, task5, task6) Please any one help me out, where i am going wrong.

4
  • Since the tasks are executed in parallel, why to you expect the same order as the serial ones? Commented Mar 7, 2018 at 5:56
  • because Tasks in concurrent queues are guaranteed to start in the order they were added. Commented Mar 7, 2018 at 6:00
  • 2
    Yes, but the thread start and the print call are separated, the thread can start but it can be put to sleep before it ends up executing the print call Commented Mar 7, 2018 at 6:01
  • Its clear now, thank you Cristik, Commented Mar 7, 2018 at 6:22

1 Answer 1

1

Bottom line, GCD will always start the tasks on a queue in the order that they were dispatched to that queue. In the case of a serial queue, that means that they will run sequentially, in that order, and this behavior is easily observable.

In the case of a concurrent queue, however, while it will start the tasks in the queued order, for tasks that are dispatched quickly in succession, they may all start quickly in succession, too, running concurrently with each other. In short, they may start running at nearly the same time, and you therefore have no assurances which will encounter its respective print statement first. Just because the concurrent queue started one task a few milliseconds after another, that provides no assurances regarding the order that those two tasks encounter their respective print statements.

In short, instead of deterministic behavior for the sequence of the print statements, you have a simple race with non-deterministic behavior.


As an aside, while it's clear that your example introduces races when employed on a concurrent queue, it should be noted that because of your nested dispatch statements, you'll have race conditions on your serial queue, too. It looks like the sequence of behavior is entirely predictable on serial queue, but it's not.

Let's consider a simplified version of your example. I'm assuming that we'll start this from the main thread:

queue.async {
    queue.async {
        print("task 3")
    }
    print("task 1")
}
queue.async {
    print("task 2")
}

Clearly, task 1 will be added to the queue first and if that queue is free, it will start immediately on that background thread, while the main thread proceeds. But as the code on the main thread approaches the dispatching of task 2, task 1 will start and will proceed to dispatch task 3. You have a classic race between the dispatching of task 2 and task 3.

Now, in practice, you'll see task 2 dispatched before task 3, but it doesn't take much of a delay to introduce non-deterministic behavior. For example, on my computer, if, before dispatching task 2, a Thread.sleep(forTimeInterval: 0.00005) manifested the non-deterministic behavior. But even without delays (or for loops of a certain number of iterations), the behavior is technically non-deterministic.


But we can create simple example that eliminates the races implicit in the above examples, but still illustrates the difference between serial and concurrent queue behavior that you were originally asking about:

for i in 0 ..< 10 {
    queue.async { [i] in
        print(i)
    }
}

This is guaranteed to print in order on serial queue, but not necessarily so on a concurrent queue.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.