umc
umc

Reputation: 39

how to make sure threads get unlocked properly

in this code based on my readings unlock does not make sure the threads
get unlocked in the same order they got locked. Is this statement right?. What is the best synchronization scheme to make sure the threads get unlocked in the same order they get locked?.

- (void)insert:(id)number
{
    [_lock lock];
    [_numbers insertObject:number];
    [_lock unlock];
}

- (void)insert
{
    @synchronized (self) {
        [_numbers insertObject:number];
    }
}

Upvotes: 0

Views: 338

Answers (2)

Rob
Rob

Reputation: 437592

In answer to your question, locks are generally “fair” (where any given thread won’t be starved). The notable exception is os_unfair_lock (and it’s right there in the name). But unfair locks were introduced for a reason, namely their efficiency. The use of unfair locks for synchronization is illustrated in Concurrent Programming With GCD in Swift 3 WWDC video. But generally, the fairness of the lock is not an issue in most synchronization scenarios.

That having been said, Apple suggests shifting away from locks and instead synchronizing with GCD. Locks are “significantly easier to misuse.” As that video goes on to say:

With GCD, your code will run in a scoped way, which means that you cannot forget to unlock. The other thing is that queues actually are way better integrated with the run time in Xcode in debugging tools.

GCD eliminates any ambiguity about the fairness of the synchronization, as it is obviously strictly FIFO.

So, the simple solution is to use a GCD serial queue. The more advanced solution is to use the reader-writer pattern, achieved with concurrent GCD queue where one performs reads synchronously (but concurrently with respect to other reads on that queue), but perform writes asynchronously with a barrier (not concurrent with respect to anything else on that queue).

Bottom line, while we often reach for GCD for simple synchronization, your locks are a perfectly adequate mechanism. The @synchronized directive is simple, but you don’t see it used as much because it is slightly less efficient (albeit, only observable if performing millions of synchronizations).

I might suggest the Mike Ash article Locks, Thread Safety, and Swift: 2017 Edition. Again, don’t get put off by the Swiftiness of the article or the aforementioned video, as much of it is equally applicable to Objective-C.


Needless to say, if you can, you should minimize what synchronization altogether. As Apple points out in their Threading Programming Guide: Synchronization:

Avoid Synchronization Altogether

For any new projects you work on, and even for existing projects, designing your code and data structures to avoid the need for synchronization is the best possible solution. Although locks and other synchronization tools are useful, they do impact the performance of any application. And if the overall design causes high contention among specific resources, your threads could be waiting even longer.

The best way to implement concurrency is to reduce the interactions and inter-dependencies between your concurrent tasks. If each task operates on its own private data set, it does not need to protect that data using locks. Even in situations where two tasks do share a common data set, you can look at ways of partitioning that set or providing each task with its own copy. Of course, copying data sets has its costs too, so you have to weigh those costs against the costs of synchronization before making your decision.

Upvotes: 1

matt
matt

Reputation: 535231

What is the best synchronization scheme to make sure the threads get unlocked in the same order they get locked?

The best synchronization scheme is not to use locks in the first place. Use Grand Central Dispatch instead. A serial queue does what locks do, coherently and simply and with vastly less chance of your making a mistake.

Upvotes: 1

Related Questions