Getting Started With GCD in MacRuby & Rubymotion

Grand Central Dispatch

Grand Central Dispatch is the apple way to perform concurrent programming, by using it you’re able to divide your program into peaces tasks that can be executed by a queue concurrently or serially. Since GCD is a low level C API, you can’t communicate directly with it, but MacRuby has a wrapper for that.

What are Queues?

You can think of Dispatch::Queues as workers waiting to execute undefined tasks, the can either execute tasks concurrently or serially. A Serial Queue executes a single task at once, a concurrent queue is capable to execute as many tasks simultaneously as your system allow it execute.

Creating and Managing Queues:

GCD comes with three different types of queues:
1) The Main queue: the main queue is the application aka. main thread, you can get this queue by using:

get the man queue
1
main_queue = Dispatch::Queue.main

2) Global / Concurrents queue: on pre Lion / iOS5 version of GCD there were only one concurrent queue that could have three defined priorities, but now this changed. You can create as much concurrent queues as you want that execute multiple blocks at the same time¹.

Get / Create Concurrent queues
1
2
3
4
# get the global concurrent queue on 10.6 OSX [priority can be :high, :low or :default]
queue = Dispatch::Queue.concurrent(priority=:default)
# On Lion or iOS5 
queue = Dispatch::Queue.concurrent("com.company.application.tasks")

3) Customized queues: are lightweight list of blocks which can be executed one at a time in FIFO order, they can be compared with Ruby mutex or traditional ruby thread. They are perfectly suited for synchronization mechanism without have to deal with lock and unlock.² If you want to ensure that tasks execute in a predictable order, you should use the customized queues.

Create a custom Queue
1
queue = Dispatch::Queue.new("com.company.application.task")

Submitting blocks to queues:

There are two ways to submit a block to a queue, the first is the asynchronous execution, which submits a block to a queue and returns immediately. e.g:

1
2
queue = Dispatch::Queue.concurrent('com.company.app.task')
queue.async { puts :hallo }

The second method is the Dispatch::Queue#sync which submits a block on a dispatch queue and waits until that block completes. Unlike the Dispatch::Queue#async method, the block are synchronously executed. e.g:

1
2
queue = Dispatch::Queue.concurrent('com.company.app.task')
queue.async { puts :hallo }

Submitting blocks later:

Dispatch::Queue#after submits a block asynchronously to the given queue after the given delay (in seconds) is passed.

1
queue.after(0.5) { puts 'waiting for the world to change' }

Concurrently executing one block many times

Dispatch::Queue#apply submits a block to a dispatch queue for multiple execution, if the execution queue is a concurrent, the block will be executed concurrently.

1
2
3
4
queue = Dispatch::Queue.concurrent('com.company.app.task')
@result = []
queue.apply(5) {|idx| @result[idx] = idx*idx }
p @result  #=> [0, 1, 4, 9, 16]

Managing Dispatch Objects

Dispatch Objects allow to manage the blocks execution by canceling, suspend and resume it.

Suspending and resuming execution:

suspending and resuming execution
1
2
3
4
5
queue = Dispatch::Queue.new('com.company.app.task')
queue.async { sleep 1; puts :hallo }
queue.suspend!
queue.suspended?
queue.resume!

Getting the internal Queue Object:

Sometimes when dealing with Cocoa / CocoaTouch API’s you will need to have access to the Queue object, for this purpose Macruby’s GDC-Wrapper delievers a method to get the queue object: Dispatch::Queue#dispatch_object

Dispatch Constants

Dispatch::TIME_FOREVER: means infinity, queue or semaphore will wait till blocks are done
Dispatch::TIME_NOW: means zero, queue or semaphore will not wait for blocks at all

Coming next

Dispatch Barrier

Dispatch Group

Dispatch Semaphore

Dispatch Source

Recommendation:

- An Introduction to GCD with MacRuby by Patrick Thomson
-Intro to Grand Central Dispatch, Part I: Basics and Dispatch Queues by Mike Ash

¹ Concurrently executed blocks may complete out of order
² the queue names/labels are meant to help you debugging your application, and it should follow the reverse-DNS style convention