9
votes

I have a serial dispatch queue created with:

dispatch_queue_t serialQueue = dispatch_queue_create("com.unique.name.queue", DISPATCH_QUEUE_SERIAL);

I want to use this serial queue to ensure thread safety for class access, while automatically doing work asynchronously that doesn't need to return to the calling thread.

- (void)addObjectToQueue:(id)object
{
    dispatch_async(serialQueue, ^{
        // process object and add to queue
    }); 
}

- (BOOL)isObjectInQueue:(id)object
{
    __block BOOL returnValue = NO;
    dispatch_sync(serialQueue, ^{
        // work out return value
    });
    return returnValue;
} 

If I call the addObjectToQueue: method, then immediately call the isObjectInQueue: method, are they guaranteed to be executed in the same order, or will/could the isObjectInQueue execute first?

In other words, does dispatch_async perform exactly the same as dispatch_sync (scheduling the block immediately) except that it doesn't block the calling thread?

I have seen similar questions with answers going both ways, so I am looking for a definitive answer, preferably backed with Apple documentation.

2
"If I call the addObjectToQueue: method, then immediately call the isObjectInQueue: method, are they guaranteed to be executed in the same order, or will/could the isObjectInQueue execute first?" by asking this you probably mean "Could we ask for the object before it was added to queue?". Because, obviously, 2 methods run in order they are called if called on same thread.krafter

2 Answers

13
votes

Are they guaranteed to be executed in the same order?

Yes.

Will / could the isObjectInQueue execute first?

Yes.

The reason for the yes to both answers is you should consider threading. Which is presumably why you are using the serial queue in the first place. You are making access to that queue thread safe.

Basically, the blocks will execute in the order in which they are put on the serial queue. That is 100% guaranteed. However, if multiple threads are hammering away at this then one thread may get in first to read something from the queue before another has had chance to add it.

In other words, does dispatch_async perform exactly the same as dispatch_sync (scheduling the block immediately) except that it doesn't block the calling thread?

That's right. In both cases the block is added to the queue. It is added immediately. dispatch_sync just waits for the block to finish before returning whereas dispatch_async returns immediately.

2
votes

I guess your question is, will the main thread keep running while dispatch_async is still executing the queue operation? I assume it won't, because that would deserve a explicit mention. If anything, I found this in dispatch_async.3 which suggests this is the case:

Conceptually, dispatch_sync() is a convenient wrapper around dispatch_async() with the addition of a semaphore to wait for completion of the block, and a wrapper around the block to signal its completion.

And indeed, if you follow the source code for dispatch_async in queue.c you'll see that the block is queued on the foreground, and only after that, execution returns to the code that called dispatch_async. Therefore if the queue is serial, dispatch_async followed by a dispatch_sync from the same thread will queue the blocks in order.

Because dispatch_sync will block until the block (and all blocks before in a serial queue) are done executing, then your code would be right. isObjectInQueue: will correctly report if the object added before is in the queue.

edit: on a multithreaded environment I would write the code above as:

- (void)addObjectToQueue:(id)object
{
    dispatch_barrier_async(_queue, ^{
        // process object and add to queue
    });
}

- (BOOL)isObjectInQueue:(id)object
{
    __block BOOL returnValue = NO;
    dispatch_sync(_queue, ^{
        // work out return value
    });
    return returnValue;
} 

because execution of each method can be deferred at any point in favor of another thread.