Adrian
Adrian

Reputation: 16735

Accessing main queue via DispatchGroup vs. DispatchQueue

I'm using DispatchGroup in a class that runs on a background thread. Occasionally, I need to update the UI, so I call the following code:

dispatchGroup.notify(queue: .main) {
  self.delegate?.moveTo(sender: self, location: location)
  self.delegate?.updateLabel(sender: self, item: self.currentItem)
}

Unfortunately, nothing happens. However, if I call the same code, via DispatchQueue.main.async { }, like so:

DispatchQueue.main.async {
  self.delegate?.moveTo(sender: self, location: location)
  self.delegate?.updateLabel(sender: self, item: self.currentItem)
}

...the delegate call gets made. I was under the impression dispatchGroup.notify(queue: .main) { } is equivalent to DispatchQueue.main.async { }.

Why are these not the same?

Upvotes: 4

Views: 3359

Answers (1)

Dan Karbayev
Dan Karbayev

Reputation: 2930

Is your dispatchGroup empty (i.e. have no blocks running) at the time when you call notify(queue:)? If not, as documentation states dispatchGroup.notify(queue:)

Schedules a work item to be submitted to a queue when a group of previously submitted block objects have completed.

Which means that your closure will be executed only after the last leave() call, when the group becomes empty. And, of course, enter()s and leave()s must be balanced.

Consider the following example:

let group = DispatchGroup()

group.enter()
someLongRunningTask() {
  // completion callback
  group.leave()
}

group.enter()
anotherLongRunningTask() {
  // completion callback
  group.leave()
}

group.notify(queue: .main) {
  print("all set")
}

In this example all set will be printed only after two callbacks with group.leave() execute.

On the other hand, DispatchQueue.main.async() immediately submits the block to the target queue but it will not necessarily start right after that – there could be running, for example, async block with the .barrier flag.

Update: Implementation of the example above using DispatchQueue (hopefully, it makes things clear):

let group = DispatchGroup()

group.enter()
someLongRunningTask() {
  // completion callback
  group.leave()
}

group.enter()
anotherLongRunningTask() {
  // completion callback
  group.leave()
}

group.wait() // waits synchronously for the submitted work to complete
DispatchQueue.main.async {
  print("all set")
}

Upvotes: 6

Related Questions