Concurrency - Part 3: Pitfalls and Summary - Medium
Concurrency - Part 3: Pitfalls and Summary - Medium
Be part of a better internet. Get 20% off membership for a limited time
1.5K 6
This is part 3 of my concurrency series. Check out Part 1 and Part 2 if you missed
them.
Pitfalls
This situation often occurs when a high QoS queue shares a resources with a Top highlight
low QoS queue, and the low QoS queue gets a lock on that resource.
GCD resolves priority inversion by temporarily raising the QoS of the entire
queue that contains the low priority tasks which are ‘ahead’ of, or blocking,
your high priority task. It’s kind of like having cars stuck in front of an
ambulance. Suddenly they’re allowed to cross the red light just so that the
ambulance can move (in reality the cars move to the side, but imagine a
narrow (serial) street or something, you get the point :-P)
We create a starter queue (where we submit the tasks from), as well as two
queues with different QoS, then we dispatch tasks to each of these two
queues, each task printing out an equal number of circles of a specific colour
(utility queue is blue, background is white.)
Because these tasks are submitted asynchronously, every time you run the
app, you’re going to see slightly different results. However, as you would
expect, the queue with the lower QoS (background) almost always finishes
last. In fact, the last 10–15 circles are usually all white.
No surprises there
But watch what happens when we submit a sync task to the background
queue after the last async statement. You don’t even need to print anything
inside the sync statement, just adding this line is enough:
Priority inversion
The results in the console have flipped! Now, the higher priority queue
(utility) always finishes last, and the last 10–15 circles are blue.
As expected, GCD resolves this inversion by raising the QoS of the entire
queue to temporarily match the high QoS task; consequently, all the tasks on
the background queue end up running at user interactive QoS, which is higher
than the utility QoS. And that’s why the utility tasks finish last!
Side-note: If you remove the starter queue from that example and submit
from the main queue instead, you will get similar results, as the main queue
also has user interactive QoS.
Although it’s not always ideal, you can minimize priority inversions by
sticking to the default QoS when creating private queues or dispatching to
the global concurrent queue.
Thread explosion
When you use a concurrent queue, you run the risk of thread explosion if
you’re not careful. This can happen when you try to submit tasks to a
concurrent queue that is currently blocked (e.g. with a semaphore, sync, or
some other way.) Your tasks will run, but the system will likely end up
spinning up new threads to accommodate these new tasks, and threads
aren’t cheap.
This is likely why Apple suggests starting with a serial queue per subsystem
in your app, as each serial queue can only use one thread at a time.
Remember that serial queues are concurrent in relation to other queues, so
you still get a performance benefit when you offload your work to a queue,
even if it isn’t concurrent.
Race conditions
Swift Arrays, Dictionaries, Structs, and other value types are not thread-safe
by default. For example, when you have multiple threads trying to access
and modify the same array, you will start running into trouble.
It’s a tricky problem because your app can run 10 times without issues, and
then it crashes on the 11th time. One very handy tool for this situation is the
Thread Sanitizer in Xcode. Enabling this option will help you identify
potential race conditions in your app.
One of the async tasks is modifying the array by appending values. If you try
running this on your simulator, you might not crash. But run it enough times
(or increase the loop frequency on line 7), and you will eventually crash. If
you enable the thread sanitizer, you will get a warning every time you run
the app.
To deal with this race condition, we are going to add an isolation queue that
uses the barrier flag. This flag allows any outstanding tasks on the queue to
finish, but blocks any further tasks from executing until the barrier task is
completed.
We have added a new isolation queue, and restricted access to the private
array using a getter and setter that will place a barrier when modifying the
array.
The getter needs to be sync in order to directly return a value. The setter can
be async , as we don’t need to block the caller while the write is taking place.
We could have used a serial queue without a barrier to solve the race
condition, but then we would lose the advantage of having concurrent read
access to the array. Perhaps that makes sense in your case, you get to decide.
Conclusion
Thank you so much for reading this series! I hope you learned something
new along the way. I will leave you with a summary and some general advice
Summary
Queues always start their tasks in FIFO order
Matt Diephouse
@mdiep · Follow
42 Reply Share
Read 1 reply
When you apply concurrency with that philosophy in mind, I think it will
help you achieve concurrent code that can be reasoned about without getting
lost in a mess of callbacks.
Besher Al Maleh
Thanks for reading. If you enjoyed this article, feel free to hit that clap
button to help others find it. If you *really* enjoyed it, you can clap up to
50 times
almaleh/Dispatcher
This is the companion app to article on concurrency in iOS. The app
is optimized for iPhone X screen or larger, so I…
github.com
Further reading:
Introduction
Concurrency is the notion of multiple things happening at the same time. With the
proliferation of multicore CPUs and…
developer.apple.com
Khanlou
Grand Central Dispatch, or GCD, is an extremely powerful tool. It
gives you low level constructs, like queues and…
khanlou.com
WWDC Videos:
1.5K 6
594 Followers
You don’t (always) need [weak self] High performance drawing on iOS
—Part 2
This article covers two different ways to
perform 2D drawing while leveraging the GP…
Mar 4 1 Jan 25
Lists
Mar 31 5 Mar 25 30
Jun 23 4 Mar 4 49
Help Status About Careers Press Blog Privacy Terms Text to speech Teams