Concurrency Flashcards
Should I use GCD or Operations?
GCD tends to be simpler to work with for simple tasks you just need to execute and forget.
Operations provide much more functionality when you need to keep track of a job or maintain the ability to cancel it.
Serial and concurrent queues
Serial queues only have a single thread associated with them and thus only allow a single task to be executed at any given time.
A concurrent queue, on the other hand, is able to utilize as many threads as the system has resources for. Threads will be created and released as necessary on a concurrent queue.
Synchronous and asynchronous tasks
When running a task synchronously, your app will wait and block the current run loop until execution finishes before moving on to the next task.
Alternatively, a task that is run asynchronously will start, but return execution to your app immediately. This way, the app is free to run other tasks while the first one is executing.
Operations
Has states
BlockOperation
BlockOperation subclasses Operation for you and manages the concurrent execution of one or more closures on the default global queue
Block operations run concurrently. If you need them to run serially, you’ll need to setup a dispatch queue instead.
Quality of service queues
◦ .userInteractive
◦ .userInitiated
◦ .utility
◦ .background
◦ .default and .unspecified
Global queues
Global queues are always concurrent and first-in, first-out.
When you add a higher task to global queue ->
You can specify a QoS yourself, but as soon as you’ll add a task with a higher QoS, your queue’s QoS service will be increased to match it.
You never want to execute something synchronously against
the main queue
When you want a DispatchGroup?
when you want to track the completion of a group of tasks.
DispatchGroup features
notify(queue:) {}
Synchronous waiting vs enter and leave methods
.notify(queue:) {} feature in DispatchGroup
DispatchGroups provide a notify(queue:) method, which you can use to be notified as soon as every job submitted has finished.
Synchronous waiting vs enter and leave methods in DispatchGroup
Sync waiting blocks the current thread
Every time you enter, the count goes up by 1. When you leave, the count goes down by 1
queue.dispatch(group: group) { // count is 1 group.enter() // count is 2 someAsyncMethod { defer { group.leave() } // Perform your work here, // count goes back to 1 once complete } }
you will usually want to use a defer statement, as shown above, so that, no matter how you exit the closure, the group.leave() code executes. regardless of how it exits, you’ve got to tell the dispatch group that the task has completed
When you want use semaphores?
you may wish to limit how many tasks happen at once.
How to use semaphores?
◦ semaphore.wait()
◦ defer { semaphore.signal() }
Asynchronous doesnʼt mean concurrent
While the difference seems subtle at first, just because your tasks are asynchronous doesn’t mean they will run concurrently. You’re actually able to submit asynchronous tasks to either a serial queue or a concurrent queue. Being synchronous or asynchronous simply identifies whether or not the queue on which you’re running the task must wait for the task to complete before it can spawn the next task.
a task being synchronous or not speaks to the source of the task. Being serial or concurrent speaks to the destination of the task.
you should never find yourself needing to create a thread explicitly.
The OS will handle all thread creation for you using higher abstractions.
If you find yourself calling the sync method, instead of the async method,
think once or twice whether that’s really what you should be doing.
Concurrency Problems
Deadlock
Race conditions
Priority inversion
How to avoid Race conditions?
Use serial queue or use Thread barrier
Thread barrier
.async(flags: .barrier)
Once the barrier hits, the queue pretends that it’s serial and only the barrier task can run until completion. Once it completes, all tasks that were submitted after the barrier task can again run concurrently.