I've just started to learn a little bit more about Grand Central Dispatch in the Swift programming language.
I followed a tutorial online to better understand GCD and tried various examples of usage...
in the section about Work Item I wrote the following code :
func useWorkItem() {
    var value = 10
    let workItem = DispatchWorkItem {
        value += 5
    }
    workItem.perform()
    let queue = DispatchQueue.global(qos: .utility)
    queue.async(execute: workItem)
    workItem.notify(queue: DispatchQueue.main) {
        print("value = ", value)
    }
}
the code basically perform the workItem in two different queues (the main and global queue) and when the work item finish running in both queues I get the result.
the output of the code above is : 20.
when I tried to manipulate the code a little bit and added another Queue to the mix and ran the same workItem with the same qos as the global queue (.utility), like so : 
 func useWorkItem() {
    var value = 10
    let workItem = DispatchWorkItem {
        value += 5
    }
    workItem.perform()
    let queue = DispatchQueue.global(qos: .utility)
    queue.async(execute: workItem)
    let que = DispatchQueue(label: "com.appcoda.delayqueue1", qos: .utility)
    que.async(execute: workItem)
    workItem.notify(queue: DispatchQueue.main) {
        print("value = ", value)
    }
}
the app crashes.
but when I change the order of the commands so I move the workItem.notify method to the beginning of the method, the app works and give me correct output which is 25 :
func useWorkItem() {
    var value = 10
    let workItem = DispatchWorkItem {
        value += 5
    }
    workItem.notify(queue: DispatchQueue.main) {
        print("value = ", value)
    }
    workItem.perform()
    let queue = DispatchQueue.global(qos: .utility)
    queue.async(execute: workItem)
    let que = DispatchQueue(label: "com.appcoda.delayqueue1", qos: .utility)
    que.async(execute: workItem)
}
can anyone please help understand how the .notify() method really works ?
and why the order of the commands made a difference ?
thanks a lot in advance...
 
                        
That first example you share (which I gather is directly from the tutorial) is not well written for a couple of reasons:
It's updating a variable from multiple threads. That is an inherently non thread-safe process. It turns out that for reasons not worth outlining here, it's not technically an issue in the author's original example, but it is a very fragile design, illustrated by the non-thread-safe behavior quickly manifested in your subsequent examples.
One should always synchronize access to a variable if manipulating it from multiple threads. You can use a dedicated serial queue for this, a
NSLock, reader-writer pattern, or other patterns. While I'd often use another GCD queue for the synchronization, I think that's going to be confusing when we're focusing on the GCD behavior ofDispatchWorkItemon various queues, so in my example below, I'll useNSLockto synchronize access, callinglock()before I try to usevalueandunlockwhen I'm done.You say that first example displays "20". That's a mere accident of timing. If you change it to say ...
... then it will likely say "15", not "20" because you'll see the
notifyfor theworkItem.perform()before theasynccall to the global queue is done. Now, you'd never usesleepin real apps, but I put it in to illustrate the timing issues.Bottom line, the
notifyon aDispatchWorkItemhappens when the dispatch work item is first completed and it won't wait for the subsequent invocations of it. This code entails what is called a "race condition" between yournotifyblock and the call you dispatched to that global queue and you're not assured which will run first.Personally, even setting aside the race conditions and the inherently non thread-safe behavior of mutating some variable from multiple threads, I'd advise against invoking the same
DispatchWorkItemmultiple times, at least in conjunction withnotifyon that work item.If you want to do a notification when everything is done, you should use a
DispatchGroup, not anotifyon the individualDispatchWorkItem.Pulling this all together, you get something like: