2010-04-19 59 views
6

假設我有一個線程安全的Things集合(稱之爲ThingList),並且我想添加以下函數。以線程安全的方式返回指針

Thing * ThingList::findByName(string name) 
{ 
    return &item[name]; // or something similar.. 
} 

但是通過這樣做,我已經委派線程安全調用代碼的責任,這將不得不做這樣的事情:

try 
{ 
    list.lock(); // NEEDED FOR THREAD SAFETY 
    Thing *foo = list.findByName("wibble"); 
    foo->Bar = 123; 
    list.unlock(); 
} 
catch (...) 
{ 
    list.unlock(); 
    throw; 
} 

顯然,一個RAII鎖定/解鎖對象將簡化/刪除try/catch/unlocks,但調用者仍然很容易忘記。

有我看了幾個備選方案:由價值

  • 回報的事情,而不是 指針 - 精細除非你需要 修改事
  • 添加功能ThingList::setItemBar(string name, int value) - 罰款,但這些傾向於 激增
  • 返回一個類似指針的對象,它在創建時鎖定列表並在銷燬時再次解鎖它。不知道這是好還是壞的做法...

什麼是正確的方法來處理這個問題?

回答

4

沒有一個「正確的方法」;這取決於您的應用程序的需求。

如果可能的話,按價值返還東西,或者返回調用者可以隨意做的任何副本。

上面的一個變體是返回一個可修改的副本,然後提供一種方法將修改後的對象原子合併到列表中。例如:

Thing t = myThingList.getThing(key); 
t.setFoo(f); 
t.setBar(b); 
myThingList.merge(t);  // ThingList atomically updates the appropriate element 

但是,如果多個線程嘗試更新同一個對象,則會導致問題。

「指針式對象」的想法聽起來很酷,但我懷疑它會導致難以發現的錯誤,當某些鎖沒有得到釋放的地方。

我會盡量保持所有的鎖定/解鎖代碼在ThingList之內,所以ThingList::set...函數可能是我會做的。

+0

「合併」是不錯後是安全的,但有一個隱藏的假設,即事情的對象知道如何找到自己的名單。你也可以通過「key」來合併,但是你有合併已被刪除的項目的問題...... – Roddy 2010-04-20 18:45:52

3

存儲和返回的boost :: shared_ptr的小號

您可以訪問期間鎖定,但你解鎖

+1

boost :: shared_ptr不會保護集合的線程安全。 – 2010-04-19 21:45:26

+0

它完全取決於您嘗試處理的線程安全問題。在訪問集合時(閱讀或寫入),你必須鎖定,但你必須這樣做。通過返回一個共享的ptr,你至少可以保證如果集合在你後面改變了,你仍然可以(假設這個集合也是shared_ptrs的集合)。這是我一直在多線程服務器上使用的模型 – pm100 2010-04-19 22:48:14

+0

我不知道shared_ptr是線程安全的,但考慮它沒有理由不可能。我知道微軟的COM使用InterlockedIncrement和InterlockedDecrement來做一個非常輕量級的線程安全實現。 – 2010-04-19 23:18:53