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).
:ok: