I need to protect a critical area of my code, which is multi-threaded. I want to prevent it from being called multiple times before the other thread is finished. This is what I am working with:
- (void) filterAllEventsIntoDictionary{
// start critical area
if (self.sortedKeys.count != 0) {
[self.sortedKeys removeAllObjects];
}
dispatch_async(self.filterMainQueue, ^{
[self internal_filterAllEventsIntoDictionary];
dispatch_sync(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
Since the internal_filterAllEventsIntoDictionary
method also accesses self.sortedKeys
, if this code is called twice, it crashes because of removeAllObjects
at the start.
I still need to call the internal...
method in another thread since I don't want to block the UI. So what's the best way to block on the start of this method while the dispatch_async
call is still not finished?
While I am far from being a concurrency expert, it sounds to me like you need a lock on your
sortedKeys
object. If you used a traditional lock, though, you'd end up blocking the main thread.The recommended replacement for locks in the world of Grand Central Dispatch is to put critical sections of code on a serial queue. See "Eliminating Lock-Based Code" in the Concurrency Programming Guide.
If you put the
[self.sortedKeys removeAllObjects];
call onto the same queue that the block with theinternal...
call is scheduled on, you guarantee that it won't happen until after that block completes:This assumes that
filterMainQueue
is serial. Usingdispatch_async
for the critical section ensures that the main thread will not be blocked. Also note the warning in "Dispatch Queues and Thread Safety":Although this will only be an issue if the
internal...
method does something that causes this method to be called again.