Search code examples
iosgrand-central-dispatchdispatch-async

Why does DispatchQueue not respect QoS?


I'm trying to run multiple tasks on the main queue, some more urgent than others. I'm using DispatchQoS to do this:

func enqueue(_ prio: Int) {
  let qos = DispatchQoS(qosClass: .userInteractive, relativePriority: prio)
  DispatchQueue.main.async(qos: qos) {
    NSLog("\(prio)")
  }
}

for i in 1...5 {
  for prio in -15...0 {
    enqueue(prio)
  }
}

I expected to see five zeros, followed by five -1's, followed by five -2's, and so on.

But I got:

-15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0

In other words, the dispatch queue is executing tasks in the order they're enqueued, ignoring priority.


I then tried different QoS classes instead of different relativePriority:

func enqueue(_ qos: DispatchQoS) {
  DispatchQueue.main.async(qos: qos) {
    NSLog("\(qos.qosClass)")
  }
}

for i in 1...10 {
  enqueue(.background)
  enqueue(.utility)
  enqueue(.userInitiated)
  enqueue(.userInteractive)
}

But the tasks again executed in the order of enqueueing, ignoring QoS.

How do I get the dispatch queue to respect QoS? My goal is to run some tasks on the main queue at a lower priority than others.


Solution

  • The solution turned out to be to use OperationQueue:

    let operation = BlockOperation(block: self.doSomething)
    
    operation.queuePriority = .low
    
    OperationQueue.main.addOperation(operation)
    

    Then I can continually execute low priority tasks, one after another, without making the UI unresponsive.

    Thanks to Rob for pointing out why DispatchQueues weren't respecting priorities.