Skip to content

Instantly share code, notes, and snippets.

@nguyentruongky
Created April 13, 2017 03:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nguyentruongky/ff8c5fd5d42ff05cbc7e4cf0040ca03a to your computer and use it in GitHub Desktop.
Save nguyentruongky/ff8c5fd5d42ff05cbc7e4cf0040ca03a to your computer and use it in GitHub Desktop.
A small note about GCD

How can concurrency programming help me?

This post isn't a tutorial, it's only a note. What do I have to remember and understand about concurrency programming. The demo project can be found here

Without concurrency, all tasks run in the main thread, and the app will be freezed when a heavy task is(Eg: downloading, uploading, image processing...) running. So, concurrency can give the users a better experience. The heavy tasks are running in background and they can do anything they want on the UI as usual.

There are 2 concurrency concepts: dispatch queues and NSOperationQueues

GCD - Grand Central Dispatch

GCD conform with the FIFO order. GCD will run tasks in separate queues, not the main queue so that your app won't be freezed. There are 2 dispatch queues: serial queuese and concurrency queues.

Serial Queues

The serial queue can only run a task at a time. The second task will run right after the first task has been done. Fortunately, we can create many serial queues and run them concurrently.

Serial queues are useful for manageing shared resources and avoiding race condition. In addition, the advantage of serial queues I like the most is tasks are executed in a predictable order. They executed in the same order as they are inserted.

In below demo, I downloaded 4 images by using serial queue.

func runSerialQueue() {
    
    let serialQueue = dispatch_queue_create("imagesQueue", DISPATCH_QUEUE_SERIAL)
    dispatch_async(serialQueue) { self.runDownloadTask(0) }
    dispatch_async(serialQueue) { self.runDownloadTask(1) }
    dispatch_async(serialQueue) { self.runDownloadTask(2) }
    dispatch_async(serialQueue) { self.runDownloadTask(3) }
}

You can create more queues and add task to new queues. Those queues will run concurrently with imagesQueue.

And here is download function:

func runDownloadTask(index: Int) {
    let img = self.downloadImage(self.imageUrl[index])
    dispatch_async(dispatch_get_main_queue(), {
        self.images[index].image = img
        self.downloadCompleted()
    })
}

Concurrent Queues

Concurrent queues execute multiple tasks at a time. You will not know the order of execution, execution time.

Besides the main queue, the system provides four concurrent queues.

  • DISPATCH_QUEUE_PRIORITY_HIGH
  • DISPATCH_QUEUE_PRIORITY_DEFAULT
  • DISPATCH_QUEUE_PRIORITY_LOW
  • DISPATCH_QUEUE_PRIORITY_BACKGROUND

Here is how to download images with concurrent queues

func runConcurrentQueue() {
    let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    dispatch_async(queue) { self.runDownloadTask(0) }
    dispatch_async(queue) { self.runDownloadTask(1) }
    dispatch_async(queue) { self.runDownloadTask(2) }
    dispatch_async(queue) { self.runDownloadTask(3) }
}

Operation Queue

Here are how operation queues are different from dispatch queues:

  • Don’t follow FIFO: in operation queues, you can set an execution priority for operations and you can add dependencies between operations which means you can define that some operations will only be executed after the completion of other operations.
  • By default, they operate concurrently: while you can’t change its type to serial queues, there is still a workaround to execute tasks in operation queues in sequence by using dependencies between operations.
  • Operation queues are instances of class NSOperationQueue and its tasks are encapsulated in instances of NSOperation.

The advantages of NSOperation

  • First, they support dependencies through the method addDependency(op: NSOperation) in the NSOperation class. When you need to start an operation that depends on the execution of the other, you will want to use NSOperation.

  • You can decide the operation priority which higher priority will be executed first.
  • You can cancel an operation after it's added to the queue.

Let's see how it works.

 func runNSOperationQueue() {
    queue = NSOperationQueue();
    func createOperation(index: Int) -> NSBlockOperation {
        let operation = NSBlockOperation(block: { self.runDownloadTask(index) })
        operation.completionBlock = {
            print("Operation \(index + 1) completed, cancelled:\(operation.cancelled)")
        }
        return operation
    }

    let operation1 = createOperation(0)
    queue.addOperation(operation1)

    let operation2 = createOperation(1)
    operation2.addDependency(operation1)
    queue.addOperation(operation2)

    let operation3 = createOperation(2)
    operation3.addDependency(operation2)
    queue.addOperation(operation3)
    
    let operation4 = createOperation(3)
    queue.addOperation(operation4)
}

We can see operation 2 (op2) depends on op1 and op3 depends on op2. It means, op1 and op4 run at a time and op1 completed, op2 runs and same to op3.

Conclusion

Concurrency is extremely strong in building app contacted with server, downloading or processing heavy tasks. It helps our apps run incredible smoothly. This note is based on http://www.appcoda.com/ios-concurrency/. Thank AppCoda for supporting this blog.

Written on Jan 11, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment