2011-03-30 149 views
0

使用NSRecursiveLock有意等待在後臺線程上完成某個操作會保存嗎?這裏是一個例子:使用NSRecursive鎖等待操作完成

我有一個類,我想有一個可以異步或同步的loadParts函數。可能會提前調用異步函數,以便在實際需要數據之前加載這些部分。同步的應該檢查數據是否已經被加載,或者正在被加載。如果它已經被加載,它可以返回數據;如果它當前被加載,那麼它應該等待它被加載然後返回;如果它甚至沒有被加載,那麼它應該只是同步加載它。這是我正在嘗試使用的代碼:

// Private function to be run either on main thread or 
// background thread 
-(void)_loadParts 
{ 
    [_loadingPartsLock lock]; 
    _loadingParts = YES; 

    // Do long loading operation 

    _loadingParts = NO; 
    _partsLoaded = YES; 
    [_loadingPartsLock unlock]; 
} 

// Asynchronous loading of parts 
-(void)preloadParts 
{ 
    if(_loadingParts || _partsLoaded) 
     return; 

    [self performSelectorInBackground:@selector(_loadParts) withObject:nil]; 
} 

// Synchronous loading of parts 
-(void)loadParts 
{ 
    if(_loadingParts) 
    { 
     [_loadingPartsLock lock]; 
     [_loadingPartsLock unlock]; 
    } 

    if(!_partsLoaded) 
    { 
     [self _loadParts]; 
    } 
} 

這是安全/有效的方法嗎?我已經看到了一些可能的問題。在沒有鎖的情況下設置和測試BOOL的值是否是線程安全的?如果在後臺線程仍在加載的情況下調用同步函數,我也會鎖定兩次。

有沒有更常見的更好的方法來實現這個功能?

謝謝!

+0

我想這就是爲什麼我們有NSCondition。 – 2011-03-30 04:20:07

回答

0

受到bbum的回答的啓發,我找到了一個使用NSOperationQueue的解決方案,但並不像他描述的那樣。在我的preloadParts函數中,我創建並存儲了一個加載操作,它是運行我的後臺線程函數的NSInvocationOperation實例。然後我將它添加到NSOperationQueue。

如果在任何時候,數據是由另一個類請求的。我首先檢查數據是否加載(變量已設置)。如果不是,我檢查操作是否在隊列中。如果是,那麼我調用[_loadOperation waitUntilFinished]。否則,我將它添加到waitUntilFinished參數的操作隊列中。這裏是我想出了代碼:

-(void)preloadCategories 
{ 
    if([[_operationQueue operations] containsObject:_loadOperation]) 
     return; 

    [_operationQueue addOperation:_loadOperation]; 
} 

-(CCPart*)getCategoryForName:(NSString*)name 
{ 
    if(nil == _parts) 
    { 
     [self loadCategories]; 
    } 
    return [_parts objectForKey:name]; 
} 

-(void)loadCategories 
{ 
    if(nil != _parts) 
     return; 

    if([[_operationQueue operations] containsObject:_loadOperation]) 
    { 
     [_loadOperation waitUntilFinished]; 
    } 
    else 
    { 
     [_operationQueue addOperations:[NSArray arrayWithObject:_loadOperation] 
        waitUntilFinished:YES]; 
    } 
} 

-(void)_loadCategories 
{ 
    // Function that actually does the loading and sets _parts to be an array of the data 
    _parts = [NSArray array]; 
} 

在初始化函數我設置_operationQueue和_loadOperation如下:

_operationQueue = [[NSOperationQueue alloc] init]; 
      _loadOperation = [[NSInvocationOperation alloc] initWithTarget:self 
                    selector:@selector(_loadCategories) 
                    object:nil]; 
3

遠遠更好的解決方案是使用dispatch_queueNSOperationQueue(配置爲串行操作)。

排隊您的加載操作,然後排隊完成後應該發生的任何事情。如果「完成」操作是「告訴主線程更新」,那很好 - 在主線程上執行一個方法,該方法實際上是觸發更新以響應現在加載的數據的事件。

這可以避免與完全鎖定相關的問題和開銷,同時還可以在不需要某種輪詢機制的情況下解決「完成」通知問題。

+0

感謝您的回答。當數據完成加載時,我並不想要執行正確的操作。大多數情況下,這個操作將被預先調用並且不會有問題(數據將有足夠的時間加載)。我只是想要一個額外的預防措施,如果數據沒有完成加載,它會(最壞的情況下)阻塞線程,直到它完成。這是一個公共類,我正在做一個框架,所以我不希望用戶不必擔心註冊回調或類似的東西。 – drewag 2011-04-08 03:29:09