I defined obj-C method:
- (id)init {
self = [super init];
if(self) {
sem = dispatch_semaphore_create(1);
}
return self;
}
- (void)f {
dispatch_block_t b = ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
//do something
dispatch_semaphore_signal(sem);
};
dispatch_async(a, b); //MUST be dispatched to global concurrent queue,
//referenced by a
}
Let's suppose that f
is called concurrently from 3 different threads t1, t2, t3 in that order, i.e. emulating serial execution.
Is it correct to suggest that dispatch_semaphore_signal() will be signalling in the same order?
If not then how can I ensure that the function is called in serial manner considering the limitations?
You use the semaphore like a basic concurrency lock. This means that your code is equivalent to
- (id)init{
self=[super init];
if(self){
lock = [NSLock new];
}
return self;
}
- (void)f{
dispatch_block_t b= ^{
[lock lock];
//do something
[lock unlock];
};
dispatch_async(a, b); //MUST be dispatched to global concurrent queue,
//referenced by a
}
For this use case you usually use serial dispatch queues. Because it is much easier this way.
- (id)init{
self=[super init];
if(self){
queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)f{
dispatch_async(queue, ^{
// do something
});
}
However, back to your question. The blocks will usually be called in the same order that you schedule them, but only until there is load and it really matters. The only guarantee is that the code in // do something
will not be executed concurrently.
GCD guarantees that blocks are starting to execute in the same order that they were scheduled. This is even true for concurrent queues. To see why this doesn't mean that // do something
is called in the appropriate order, consider this:
Thread 1 schedules t1
Thread 2 schedules t2
t1 starts executing
t2 starts executing
t1 gets suspended because of high CPU load
t2 runs dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)
Thread 3 schedules t3
t2 succeeds and starts with `// do something`
t1 gets unsuspended and calls dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)
t1 now blocks
...
In this scenario (which will happen some day), t1 begins executing before t2, but still the // do something
block from t2 gets executed first. Depending on your use case, this may or may not be a problem for you. If you need total ordering, you need to use a serial dispatch queue (recommended) or create your own locking scheme (not recommended unless you really need to.)