2011-04-23 255 views
1

我在寫一個Objective-C類,我想要線程安全。要做到這一點,我使用pthreads和pthread_rwlock(使用@synchronized是矯枉過正,我想了解更多關於pthreads)。該鎖定在指定爲init方法的對象中,並在dealloc中銷燬。我有三種操作鎖定的方法; readLockwriteLockunlock。這三個方法只是簡單地調用相關的pthread函數,目前沒有其他的東西。如何確定線程是否有鎖?

下面是的對象的方法,兩者都需要一個writeLock二:

-(void)addValue:(const void *)buffer 
{ 
    [self writeLock]; 

    NSUInteger lastIndex = self.lastIndex; 
    [self setValue:buffer atIndex:(lastIndex == NSNotFound) ? 0 : lastIndex+1]; 

    [self unlock]; 
} 


-(void)setValue:(const void *)buffer atIndex:(NSUInteger)index 
{ 
    [self writeLock]; 
    //do work here 
    [self unlock]; 
} 

setAddValue:調用將首先獲得寫入鎖定,然後調用setValue:atIndex:這也將試圖獲得一個寫鎖。該文檔指出,發生這種情況時行爲是不確定的。因此,如何在嘗試獲取鎖之前檢查線程是否有鎖? (我可以確保臨界區不會觸發另一個鎖請求,但這意味着代碼重複,並且我想讓我的代碼幹掉)。

回答

2

不完全清楚你正在使用什麼樣的鎖。你指出你正在使用pthreads,並且讀/寫鎖,所以我得出結論說你正在使用pthread_rwlock。

如果是這樣,那麼你應該可以在鎖上使用pthread_rwlock_trywrlock。從手冊頁開始,

如果成功,則pthread_rwlock_wrlock()和pthread_rwlock_trywrlock()將返回零。否則,將返回一個錯誤號碼 以表明錯誤。

而且,一個錯誤的是:

[EDEADLK]調用線程已經擁有讀/寫鎖定 (讀或寫)。

因此,我認爲你應該能夠調用pthread_rwlock_trywrlock(),你要麼是成功的,它會返回EBUSY如果另一個線程已鎖定,或者你會得到EDEADLK如果當前線程具有鎖。

+0

該信息稍微正確。 'EDEADLK'錯誤只針對'pthread_rwlock_(rd | wr)鎖定'指定,而不是trylock函數,在這種情況下它將返回'EBUSY',並且不區分鎖定是由調用線程還是其他線程持有。此外,由同一個線程保持的鎖只有在寫入鎖定時才能檢測到。由於可以有無限數量的閱讀器,所以持有鎖的閱讀器的身份不能被檢查。然而,在OP的情況下,它看起來像一個雙重寫鎖是需要避免的問題,所以它可能是好的。 – 2011-04-23 21:25:05

+0

此外,'EDEADLK'錯誤只是一個「可能」,而不是「應該」,即不需要執行檢測和報告 – 2011-04-23 21:26:45

+0

在進一步的考慮中,我認爲您的解決方案不可行。我已經發布了一個替代方案作爲答案。 – 2011-04-23 21:32:44

0

首先,僅包含一個操作的關鍵部分是無用的。關鍵在於使不同的事物相互同步。 (你確實有效地使整數原子,但這可能不是完整的意圖。)

其次,你已經知道你在後面的關鍵部分有寫鎖定,所以沒有必要檢查它是否存在或不。寫作時不要嘗試讀鎖。

解決方案可能會將readLockwriteLock調用轉移到調用函數中,但不知道更多情況是不可能的。

(這也可能會通過減少總的操作次數,你將不會被鎖定,然後立即解鎖減少鎖定的性能開銷。也許你並不需要直接在並行線程級的工作。)

+0

第一句話是危險錯誤的。在C中沒有一個單一的原子操作。如果你正在編寫x86彙編,並且使用帶有'lock'前綴的內存操作指令,那麼你會是正確的,但是否則你需要鎖定。 – 2011-04-23 21:34:24

+0

@R ..:我說他*鎖定的效果是使整數原子。或者至少,他*是做的。自從我寫回答以來,問題發生了變化(奇怪的是,自從他接受了另一個答案以來)。 – Potatoswatter 2011-04-23 21:56:36

0

可移植程序不能依賴實現來告訴調用者它已經擁有寫入鎖定。相反,你需要做這樣的事情來包裝rwlocks用遞歸寫鎖:

int wrlock_wrap(pthread_rwlock_t *l, int *cnt) 
{ 
    int r = *cnt ? 0 : pthread_rwlock_wrlocK(l); 
    if (!r) ++*cnt; 
    return r; 
} 

int wrunlock_wrap(pthread_rwlock_t *l, int *cnt) 
{ 
    --*cnt; 
    return pthread_rwlock_unlock(l); 
} 

你可以保持pthread_rwlock_t旁邊的計數無論它的存儲,例如作爲你的struct/class/whatever的成員。

+0

關於可移植性的說明是正確的,但OP顯然在OS X上,並且在Objective C和Cocoa框架的選擇中放棄了可移植性考慮。我最初考慮的是類似於你的建議的實現(儘管直接在OP顯示的實現內部直接實現),但在檢查OS X手冊頁(參見上面的參考資料)後放棄了它。 – 2011-04-24 02:01:33