Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In GCD are serial queue synchronous by asynchronous operation Swift

I am using a serial queue with QoS background

let serialQueue = DispatchQueue(label: "queue1", qos: DispatchQoS.background)

Assigning one jobs synchronous, and two jobs asynchronous:

func serialTask() {
    serialQueue.sync {
        for i in 0..<10 {
            print("🔷", i)
        }
    }
    serialQueue.async {
        for i in 20..<30 {
            print("⚪️", i)
        }
    }

    serialQueue.async {
        for i in 101..<120 {
            print("🔷", i)
        }
    }
}

All 3 jobs are executing sync one after another, but last two jobs are async. Are async jobs sync in serial queue's.

like image 627
salman siddiqui Avatar asked Mar 13 '18 06:03

salman siddiqui


People also ask

What is synchronous vs asynchronous in GCD?

Asynchronous means out of line, synchronous means in line. You can perform synchronous tasks and block multiple threads at once. If you are in a background thread and want to update a whole bunch of the user interface you call out to the main thread in a dispatch queue.

Is global queue serial or concurrent?

Concurrent queues (also known as a type of global dispatch queue) execute one or more tasks concurrently, but tasks are still started in the order in which they were added to the queue.

Is Swift main queue concurrent?

There are two types of queues, serial and concurrent, but all queues are concurrent relative to each other. The fact that you want to run any code "in the background" means that you want to run it concurrently with another thread (usually the main thread).

What is serial queue in Swift?

A Serial queue allows us to perform only one task at a time, no matter the way of execution, i.e. Synchronous or Asynchronous. All the queues need to wait for the completion of the previous queue. By default, DispatchQueue is a serial queue. E.g. let queue = DispatchQueue(label: "com.swiftpal.dispatch.serial") queue.


1 Answers

Let me see if I can clarify the difference between async vs. sync.

A couple of changes that I will employ in my example:

  1. I will use Instruments’ “Points of Interest” to show when tasks are running rather than print statements. (See WWDC 2019 Getting Started With Instruments.) This way we can see the behavior graphically.

    I will post a simple “Point of Interest” event signpost (Ⓢ) when dispatching something and I will wrap the dispatched task in a “Region of Interest” (a horizontal bar) to graphically illustrate the duration of some process.

  2. I'll change your for loops to be a Thread.sleep(forTimeInterval: 1), simulating some time consuming process. If you just have a quick for loop, things will happen so quickly that it will be impossible to discern what's really happening with the threads.

So, consider:

import os.signpost

private let pointsOfInterest = OSLog(subsystem: "GCD Demo", category: .pointsOfInterest)

func tasks(on queue: DispatchQueue) {
    pointsOfInterestRange(with: "tasks(on:)") {
        os_signpost(.event, log: pointsOfInterest, name: "1") // first Ⓢ
        queue.sync { self.oneSecondProcess(with: "1") }

        os_signpost(.event, log: pointsOfInterest, name: "2") // second Ⓢ
        queue.async { self.oneSecondProcess(with: "2") }

        os_signpost(.event, log: pointsOfInterest, name: "3") // third Ⓢ
        queue.async { self.oneSecondProcess(with: "3") }
    }
}

func oneSecondProcess(with staticString: StaticString) {
    pointsOfInterestRange(with: staticString) {
        Thread.sleep(forTimeInterval: 1)
    }
}

func pointsOfInterestRange(with staticString: StaticString, block: () -> Void) {
    let identifier = OSSignpostID(log: pointsOfInterest)
    os_signpost(.begin, log: pointsOfInterest, name: staticString, signpostID: identifier)
    block()
    os_signpost(.end, log: pointsOfInterest, name: staticString, signpostID: identifier)
}

That is just like your example, but rather than print statement, we have signposts statements, yielding the following graphical timeline in Instruments’ “Points of Interest” tool:

enter image description here

So, you can see that:

  1. The tasks(on:) function, on the bottom, issued the sync dispatch, the first Ⓢ signpost.

  2. It waits for the sync task, “1”, to finish before continuing, at which point it issues the two subsequent dispatches, the second and third Ⓢ signposts (which happen so quickly in succession that they overlap in the graph).

  3. But tasks(on:) doesn't wait for the two async tasks, “2” and “3”, to finish. As soon as it finished dispatching those async tasks, it immediately returns (hence the tasks(on:) range stops immediately at that point).

  4. Because the background queue was serial, the three dispatched tasks (“1”, “2”, and “3”) run sequentially, one after the other.

If you change this to use a concurrent queue, though:

let queue = DispatchQueue(label: "...", attributes: .concurrent)

Then you can see that the two async tasks now run concurrently with respect to each other:

enter image description here

This time, task(on:) dispatches the sync call, waits for it to finish, and then, only when that sync call is done can seriesOfTasks proceed to dispatch the two async calls (in this case, not waiting for those to dispatched tasks to finish).

As you can see, the async and sync behavior is different. With sync the calling thread will wait for the dispatched task to finish, but with async, it won't.


There are two main conclusions that one can draw from the above:

  1. The choice of sync vs async dictates the behavior of the current thread (i.e. should it wait for the dispatched task or not).

    And, as a general rule, we would generally avoid calling sync from the main thread when doing anything time consuming (because that would end up blocking the main thread).

  2. The choice of a serial queue vs a concurrent queue dictates the behavior of the work you dispatched, namely can it run concurrently with respect to other tasks on that queue, or will they run consecutively, one after the other.

like image 69
Rob Avatar answered Sep 29 '22 12:09

Rob