0

In Mike Ash's GCD article, he mentions: "Custom queues can be used as a synchronization mechanism in place of locks."

Questions:

1) How does dispatch_barrier_async work differently from dispatch_async? Doesn't dispatch_async achieve the same function as dispatch_barrier_async synchronization wise?

2) Is custom queue the only option? Can't we use main queue for synchronization purpose?

1 Answer 1

2

First, whether a call to submit a task to a queue is _sync or _async does not in any way affect whether the task is synchronized with other threads or tasks. It only affects whether the caller is blocked until the task completes executing or if it can continue on. The _sync stands for "synchronous" and _async stands for "asynchronous", which sound similar to but are different from "synchronized" and "unsynchronized". The former have nothing to do with thread safety, while the latter are crucial.

You can use a serial queue for synchronizing access to shared data structures. A serial queue only executes one task at a time. So, if all tasks which touch a given data structure are submitted to the same serial queue, then they will never be executing simultaneously and their accesses to the data structure will be safe.

The main queue is a serial queue, so it has this same property. However, any long-running task submitted to the main queue will block user interaction. If the tasks don't have to interact with the GUI or have a similar requirement that they run on the main thread, it's better to use a custom serial queue.

It's also possible to achieve synchronization using a custom concurrent queue if you use the barrier routines. dispatch_barrier_async() is different from dispatch_async() in that the queue temporarily become a serial queue, more or less. When the barrier task reaches the head of the queue, it is not started until all previous tasks in that queue have completed. Once they do, the barrier task is executed. Until the barrier task completes, the queue will not start any subsequent tasks that it holds.

Non-barrier tasks submitted to a concurrent queue may run simultaneously with one another, which means they are not synchronized and, if they access shared data structures, they can corrupt that data structure or get incorrect results, etc.

The barrier routines are useful for read-write synchronization. It is usually safe for multiple threads to be reading from a data structure simultaneously, so long as no thread is trying to modify (write to) the data structure at the same time. A task that modifies or writes to the data structure must not run simultaneously with either readers or other writers. This can be achieved by submitting read tasks as non-barrier tasks to a given queue and submitting write tasks as barrier tasks to that same queue.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks - appreciate the clear explanation. When using dispatch_sync, does it block the entire thread (e.g. can timer still fire on a particular method in the thread while a task is running via dispatch_sync?)
It blocks the thread. The task submitted using dispatch_sync() may or may not run on the calling thread. ("May" because the thread is known not to be doing anything else at the time. "May not" because the task may need to wait for other stuff to complete if the queue is serial or running a barrier task and, when that completes, it may be more efficient to run the task on the thread that was on-CPU running that stuff than to wake the calling thread to do it and switch contexts.) If the task runs on the calling thread, it might run a run loop, which would allow a timer or other source to fire.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.