Now that Grand Central Dispatch is part of iPhone OS 4.0, I would like to showcase its unique benefits for inter-thread communication.
Passing data from one thread to another is normally a royal pain on the Mac, something involving the use of Mach ports, something I’d rather not talk about. Ever! Assuming that your threads use Core Foundation or Cocoa, your threads have a run loop and you can shuffle bits of data back and forth with no trouble whatsoever. You can even do it in C++.
We will look at running a code block in another thread and waiting for it to finish (synchronous execution), as well as running a block in another thread and having it run a callback that we supply (asynchronous execution).
Let’s grab a few required header files and get started!
1 2 3 4 5 6 | |
This is the chunk of data we will be throwing around. Feel free to expand it as you see fit.
1 2 3 4 5 | |
We want to be good memory managers and collect more than a few bits of data.
1 2 | |
A block type is different from a function pointer in that it uses a ‘hat’ () instead of a ‘star’ (*). We want to know how our operation went so we return an integer error code.
1 2 | |
We need a callback type for asynchronous execution. Once or block runs on some other thread, it will invoke our callback and pass the error code from the operation (block) that we just executed. Note that our callback is also a block.
1 2 | |
We are using C++ so let’s make another callback, one that takes both the status from the block we just executed and the bits of data we collected in the process.
1 2 | |
We will assume that there’s a single thread that we want to talk to, running somewhere, all lonely. There is nothing preventing you from passing the run loop as an argument, though. You do need to have a reference to the run loop, though, no way around it!
1 2 | |
Synchronous execution of a block uses a semaphore for synchronization. We want to tell the other thread to run the block for us and we want to sit quietly until we are told that the block has finished executing.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | |
Note that we both wait forever on the semaphore and give the run loop of the target thread a gentle bump to remind it that there’s work to do.
Running a block asynchronously is conceptually simple: we tell the other thread to run the block, the other thread tells us to run the callback once it’s done. There are no semaphores and no waiting.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | |
This is what a sample operation (block) look like. Note that we need to allocate the block on the heap rather than the stack (default). This is where Block_copy comes in. We want nothing more than to collect a few bits of data into a vector, making sure no exceptions excape the block. We will not need to change the block code for synchronous and asynchronous execution. Hurray for code reuse!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
Here is what synchronous collection of bits looks like. Note that we need to use deallocate a block and release its resources once we no longer need it. This is where Block_release comes in.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Running asynchronously requires us to run a given callback after we are done. We also must release the block we were asked to run. We work around this by creating a callback on the fly that first releases the block and then runs the callback we were given.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Let me know if you have any questions!