Let’s talks about the concurrency issue in the multithreaded application.
Multithreading is one of the most important concepts in software engineering. It comes with complexity like race conditions and deadlock and thread-safety.
Race Condition
A race condition is an undesirable situation that occurs when a program attempts to perform two or more operations at the same time on shared resources.
Data Race
A data race occurs when one thread reading data from a shared object while another thread is writing to it.
Ensure ThreadSafe
Thread-safe is a concept in the context of multi-thread and it means any shared data is accessed by only one thread at any given time.
- NSLock
- Dispatch Semaphore
- Serial Queue
- Dispatch Barrier
These are some methods to ensure the thread-safety, each one has its own pros and cons. Let's create thread-safety using dispatch barrier, one of my all-time favorites in handling concurrency.
Dispatch Barrier
A dispatch barrier allows us to create a synchronization point within a concurrent dispatch queue. Normally, the queue acts just like a normal concurrent queue, When the barrier is executing, it acts as a serial queue that creates synchronization. Once the barrier finishes, the queue goes back to being a normal concurrent queue.
Real Use Case
In our project, we have user availability which helps to know the user state. The state can be changed every second, in such cases we tend to get n number of incoming availability, in peak hours the N will be huge. so it’s very important to protect the availability source (shared) from the race condition and concurrency issues. After so many iterations, we ended up choosing the DispatchBarrier to handle the synchronization.
getAvailability
offers concurrent reading since the dispatching queue is been marked as a concurrent queue. sync
blocks the caller thread till the block execution completes. whereas on updateAvailability
the dispatching queue is marked as async
with barrier
flag, which means that it will wait until every currently running block in the queue is finished executing before it executes. Other blocks will queue up behind it and be executed when the barrier dispatch is done.
custom serial queue
is a bad choice for serialization, since barriers won’t do anything helpful since a serial queue executes one operation at a time anyway, custom concurrent queue
is a great candidate for creating automaticity.
💡 NSCache
provides default synchronization since it's from Obj-C.
Thread Sanitizer
Thread Sanitizer is a part of a family of LLVM tools that combines compile-time instrumentation and runtime monitoring to detect threading bugs in the code. It was introduced in Xcode 8 and has support for both Objective C and Swift. It can detect multiple types of threading bugs one of which is the Data Race Condition.
If your application has a Data Race Condition, Thread Sanitizer will detect it and log in to the console. (Note — Thread Sanitizer might not detect all Data Race Conditions in a single attempt. In that case, try executing the steps to reproduce multiple times)
Wrapping up:
In this article, we talk about a method for achieving thread safety. The idea behind every approach is the same, identify the critical code section and make it accessible by only one thread at a time.