HyunSu
HyunSu

Reputation: 165

is GCD really Thread-Safe?

I have studied GCD and Thread-Safe. In apple document, GCD is Thread-Safe that means multiple thread can access. And I learned meaning of Thread-Safe that always give same result whenever multiple thread access to some object.

I think that meaning of Thread-Safe and GCD's Thread-Safe is not same because I tested some case which is written below to sum 0 to 9999.

"something.n" value is not same when I excute code below several time. If GCD is Thread-Safe , Why isn't "something.n" value same ?

I'm really confused with that.. Could you help me? I really want to master Thread-Safe!!!

class Something {
    var n = 0
}
class ViewController: UIViewController {
    let something = Something()
    var concurrentQueue = DispatchQueue(label: "asdf", attributes: .concurrent)
    override func viewDidLoad() {
        super.viewDidLoad()
        let group = DispatchGroup()
        for idx in 0..<10000 {
            concurrentQueue.async(group: group) {
                self.something.n += idx
            }
        }
    
        group.notify(queue: .main ) {
            print(self.something.n)
        }
    }
}

Upvotes: 2

Views: 1277

Answers (2)

Rob
Rob

Reputation: 437422

You said:

I have studied GCD and Thread-Safe. In apple document, GCD is Thread-Safe that means multiple thread can access. And I learned meaning of Thread-Safe that always give same result whenever multiple thread access to some object.

They are saying the same thing. A block of code is thread-safe only if it is safe to invoke it from different threads at the same time (and this thread safety is achieved by making sure that the critical portion of code cannot run on one thread at the same time as another thread).

But let us be clear: Apple is not saying that if you use GCD, that your code is automatically thread-safe. Yes, the dispatch queue objects, themselves, are thread-safe (i.e. you can safely dispatch to a queue from whatever thread you want), but that doesn’t mean that your own code is necessarily thread-safe. If one’s code is accessing the same memory from multiple threads concurrently, one must provide one’s own synchronization to prevent writes simultaneous with any other access.

In the Threading Programming Guide: Synchronization, which predates GCD, Apple outlines various mechanisms for synchronizing code. You can also use a GCD serial queue for synchronization. If you using a concurrent queue, one achieves thread safety if you use a “barrier” for write operations. See the latter part of this answer for a variety of ways to achieve thread safety.

But be clear, Apple is not introducing a different definition of “thread-safe”. As they say in that aforementioned guide:

When it comes to thread safety, a good design is the best protection you have. Avoiding shared resources and minimizing the interactions between your threads makes it less likely for those threads to interfere with each other. A completely interference-free design is not always possible, however. In cases where your threads must interact, you need to use synchronization tools to ensure that when they interact, they do so safely.

And in the Concurrency Programming Guide: Migrating Away from Threads: Eliminating Lock-Based Code, which was published when GCD was introduced, Apple says:

For threaded code, locks are one of the traditional ways to synchronize access to resources that are shared between threads. ... Instead of using a lock to protect a shared resource, you can instead create a queue to serialize the tasks that access that resource.

But they are not saying that you can just use GCD concurrent queues and automatically achieve thread-safety, but rather that with careful and proper use of GCD queues, one can achieve thread-safety without using locks.


By the way, Apple provides tools to help you diagnose whether your code is thread-safe, namely the Thread Sanitizer (TSAN). See Diagnosing Memory, Thread, and Crash Issues Early.

Upvotes: 5

Shehata Gamal
Shehata Gamal

Reputation: 100503

Your current queue is concurrent

var concurrentQueue = DispatchQueue(label: "asdf", attributes: . concurrent)

which means that code can run in any order inside every async

Every run has a different order but variable is accessed only by one part at a time

Upvotes: 1

Related Questions