How to execute some code in a particular thread context?

I've run some code in a thread: AVAudioPlayerNodeImpl.CompletionHandlerQueue.

This initialises an audio recording that is scheduled to start and executed in an AVAudioPlayerNode .scheduleBuffer callback called completionHandler.

Separately, in the UI there's a button that calls a method to stop the recorder, this is executed in the main thread.

My question is if I need to use DispatchQueue and run the stop recording method in the thread AVAudioPlayerNodeImpl.CompletionHandlerQueue, if so, how do I do that? Do I have to hard-type the name?

The reason why I ask this is that if I execute UI stuff outside the main thread I get some warning messages regarding publishing outside it, so wondering about the opposite.

Notes: I'm using XCode and SwiftUI

The rule does differ from framework to framework. Since this is Apple specific framework, it’s probably better to ask over Apple’s dev forum, or stackoverflow.

There’s also an old AVFoundation guide which should still be largely relevant even though it is written in ObjC. It also contains this rule-of-thumb:

There are two general guidelines as far as notifications and threading:

  • UI related notifications occur on the main thread.
  • Classes or methods that require you create and/or specify a queue will return notifications on that queue.

It does warn (in the Media Capture section) that startRunning is blocking and so should not be invoked in the main thread. Doesn’t say anything about stop though.

Did you get this from the debugger? I don’t even think you can access this queue externally. Generally completion handlers are where you process the result (the recording has stopped, what now?) rather than the target for invocation (please stop recording). So likely you don’t need to send anything to that queue (given the hint in the name).

PS
It seems you’ve got some confusion about thread vs queue. You can search online for a quick primer with a keyword like Grand Central Dispatch.

Thanks once again @Lantua

I'll rewatch some videos about grand central dispatch and read some articles; I've re-read one now :slight_smile:

So far my understanding is gcd allows us to dispatch a closure to be executed in a particular* thread that executes at some unpredictable time when the system is available to compute it! The closures are placed in a queue block and is indexed and computed through a FIFO approach.
It seems I can only dispatch queues on main, but any other background thread is randomly selected by the queue dispatcher, the user has no control.
There's also a complicated algorithm that is responsible to manage all this, by evaluating the priority a queue block has, that is defined by the quality of service.

Hope to be able to understand this better and correct myself at some point!

You're mostly correct. Turns out tuning the actual thread parameters (how many threads, what priorities, what if we have different cpu cores, etc.) are tricky to get right. So instead the dispatch queue have you post a closure to be executed (called work item), and the GCD will figure out the threading for you. A single thread can run work items from multiple queues, and multiple threads can help run closures from the same queue, but the point is that it doesn't matter which thread it is on.

You're correct. Together with the fact that most queues are serial queue–a work item only starts after the previous one finished, the implications are:

  1. Only one closure in the queue is executed at any time. If you put all the codes that update screenTitle in the same serial queue. You can safely assume in the closure that no other code is editing screenTitle.
  2. You can be assured of the order of an execution. If you post multiple work items, it's always executed in that order. So logics like you must do A before B is easier to implement (not that it's immediately bug-free).

You're correct. You can also create a new queue using its initializer. In which case it's the same as background queue, that you have no control which thread it's running on (and that's the point). main is more of an exception here.

One thing to note is that global queue is a concurrent queue (as opposed to serial queue). So it doesn't have the benefits of serial queues I mentioned above. It however does have the qos guarantee.

The rule of thumb is that you'd get at least the qos you're asking for. The complication happens in cases like a UI update (high priority) is waiting for a background download (low priority). In which case GCD needs to threat the background download to be as important as UI update, so that it doesn't wait for too long. That's also a reason why you shouldn't run non-trivial work on the main queue.


All in all, the important thing when working with dispatch queue is that you think about the order of the work item you've posted to the queue, and which queue to post to, rather than which thread it's running on.

The nuance is important because DispatchQueue.sync is posting a new work item and then wait for it to complete as oppose to the common misconception that it simply switches to the main thread. And it could deadlock the queue if you do things like this

// On the main queue right now
DispatchQueue.main.sync {
  // Do Job A
}
// We need to wait for Job A to finish,
// but Job A can't start until we've finished
// as it's a serial queue...
1 Like

Thanks for explaining everything in such a detailed way, I'll keep on with my studies : )