Use dispatch queues for synchronization

Switch from using `@synchronized` and `NSLock` for synchronization to using dispatch queues.

* Instead of mutual exclusion with `NSLock`, use a serial queue and `dispatch_sync` around critical sections.
* Instead of `@synchronized` for mutable object synchronization, use a concurrent queue with `dispatch_sync` around reads and `dispatch_barrier_async` around writes.

@jnjosh Please review. This is arguably better than before. It’s very slightly more performant, but more importantly, it’s consistent. Also, merge this before pull request #3 (task graph completion).
See the comment in the pull request. We allow many readers but one writer. So reads are done using `dispatch_sync`. Writes are done with `dispatch_barrier_async`, which makes sure all reads are finished before executing the write, then allows reads to occur again.