6

我有一個Objective-c類的一些方法,它使用GCD隊列來確保對資源的併發訪問是以串行方式進行的(標準方式)。如何通過GCD實現objective-c中的折返鎖定機制?

其中一些方法需要調用同一類的其他方法。所以鎖定機制需要重新進入。有沒有一個標準的方法來做到這一點?

起初,我不得不每一種方法使用

dispatch_sync(my_queue, ^{ 

    // Critical section 

}); 

同步訪問。如你所知,當其中一個方法調用另一個這樣的方法時,會發生死鎖,因爲dispatch_sync調用會停止當前正在執行的內容,直到該另一個塊被執行,這也不能執行,因爲隊列上的執行被停止。爲了解決這個問題,我使用了例如這個方法:

- (void) executeOnQueueSync:(dispatch_queue_t)queue : (void (^)(void))theBlock { 
    if (dispatch_get_current_queue() == queue) { 
     theBlock(); 
    } else { 
     dispatch_sync(queue, theBlock); 
    } 
} 

而且在每一個我的方法,我用

[self executeOnQueueSync:my_queue : ^{ 

    // Critical section 

}]; 

我不喜歡這樣的解決方案,因爲對於不同的返回類型中的每個塊,我需要寫的另一種方法。而且,這個問題對我來說很常見,我認爲應該有一個更好的標準解決方案。

+0

您是否考慮過使用'@ synchronized'呢? –

+1

@MartinR,是的,但'@ synchronized'是一個經典的鎖,不是基於GCD /隊列,因此,據我所知,由於代碼簡單和性能的原因,它是不鼓勵使用它。這個問題的標題因此具有誤導性,因爲它包含「鎖定」。我的意思是以可重入的方式與GDC /隊列同步訪問。我沒有比說「可重入鎖定」更好的詞,因爲這是大多數人都知道的問題解決方案的名稱。 –

+1

@MartinR,而且'@ synchronized'的優點是可以簡化代碼重入 - 無論如何(這怎麼稱得上最好?)。儘管如此,它背後的鎖定機制比GCD隊列背後的機制性能要差一些。 –

回答

10

第一件事第一件:dispatch_get_current_queue()已棄用。規範方法現在將使用dispatch_queue_set_specific。一個這樣的例子可能看起來像:

typedef dispatch_queue_t dispatch_recursive_queue_t; 
static const void * const RecursiveKey = (const void*)&RecursiveKey; 

dispatch_recursive_queue_t dispatch_queue_create_recursive_serial(const char * name) 
{ 
    dispatch_queue_t queue = dispatch_queue_create(name, DISPATCH_QUEUE_SERIAL); 
    dispatch_queue_set_specific(queue, RecursiveKey, (__bridge void *)(queue), NULL); 
    return queue; 
} 

void dispatch_sync_recursive(dispatch_recursive_queue_t queue, dispatch_block_t block) 
{ 
    if (dispatch_get_specific(RecursiveKey) == (__bridge void *)(queue)) 
     block(); 
    else 
     dispatch_sync(queue, block); 
} 

這種模式是非常有好處的,但它無疑不是防彈的,因爲你可以創建嵌套遞歸隊列,dispatch_set_target_queue,並試圖從內一個內部排隊在外隊列工作會陷入僵局,即使你已經「鎖在裏面」(在嘲笑報價中,因爲它只有看起來就像一個鎖,它實際上是不同的東西:一個隊列 - 因此問題是對的?)。 (你可以得到周圍通過包裝調用dispatch_set_target_queue和維護自己的出帶外針對圖等等,但是這作爲練習留給讀者。)

你繼續說:

我不喜歡這個解決方案,因爲對於每個有不同 返回類型的塊,我需要編寫另一個方法。

這種「狀態保護串行隊列」模式的總體思想是你保護私有狀態;爲什麼你要「自帶隊列」呢?如果是關於共享狀態保護的多個對象,則給它們找到隊列的固有方式(即,在初始時刻將其推入,或者將其放置在所有相關方都可以訪問的地方)。目前尚不清楚「自帶隊列」如何在這裏有用。

+2

感謝您對有關棄用函數的解釋以及如何使用。這本身就很有價值。 –

+0

@ipmcc,我對你對這個和相關主題的回答和評論印象深刻。你能否也請看看這個[問題](http://stackoverflow.com/questions/20201078/how-to-implement-a-reentrant-locking-mechanism-through-dispatch-concurrent-queue)我剛剛發佈? –

+0

@ipmcc與使用帶有標籤&dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)的隊列相比,使用此方法的效率或效率是否存在差異? – Orangenhain