保护关键代码不被再次调用

时间:2011-11-17 07:10:19

标签: objective-c cocoa locking blocking grand-central-dispatch

我需要保护我的代码的关键区域,这是一个多线程的。我希望防止在其他线程完成之前多次调用它。这就是我正在使用的:

- (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];
        });
    });
}

由于internal_filterAllEventsIntoDictionary方法也会访问self.sortedKeys,如果此代码被调用两次,则会因为removeAllObjects在开始时崩溃。

我仍然需要在另一个线程中调用internal...方法,因为我不想阻止UI。那么在dispatch_async调用尚未完成时阻止此方法启动的最佳方法是什么?

1 个答案:

答案 0 :(得分:7)

虽然我不是一个并发专家,但听起来你需要锁定sortedKeys对象。但是,如果您使用传统锁,则最终会阻止主线程。

Grand Central Dispatch世界中推荐的锁替换是将关键的代码段放在串行队列中。请参阅“并发编程指南”中的"Eliminating Lock-Based Code"

如果您将[self.sortedKeys removeAllObjects];调用放在同一个队列上,并且调度了internal...调用的块,则可以保证在该块完成之后它不会发生:

// start critical area
dispatch_async(self.filterMainQueue, ^{
    if (self.sortedKeys.count != 0) {
        [self.sortedKeys removeAllObjects];
    }
});

这假设filterMainQueue serial 。将dispatch_async用于临界区可确保不会阻止主线程。另请注意"Dispatch Queues and Thread Safety"中的警告:

  

不要从传递给函数调用的同一队列上执行的任务中调用dispatch_sync函数。这样做会使队列死锁。

虽然internal...方法会导致再次调用此方法,但这只会是一个问题。