1

我真的很感激這方面的一些建議。以線程安全的方式與外部世界共享數據成員

例如

class Foo 
{ 
    TData data; 
public: 
    TData *getData() { return &data; } // How can we do this in a thread safe manner ? 
}; 

所以我想有一個機制,使getData()線程安全。我已經提出了自己的解決方案,它涉及將數據成員打包到以下模板類中,並使用互斥鎖來同步對其的訪問。你怎麼看 ?可能的問題是什麼?

class locked_object : boost::noncopyable 
{ 
    T *object; 
    TLockable *lock; 
    bool locked; 

public: 
    locked_object(T *_object, TLockable *_lock) : object(_object), lock(_lock), locked(true) 
    { 
     lock->lock(); 
    } 

    ~locked_object() 
    { 
     lock->unlock(); 
    } 

    T *get() 
    { 
     _ASSERT(locked); 
     if (!locked) 
      throw new std::exception("Synchronization error ! Object lock is already released !"); 
     return this->tobject; 
    } 


    void unlock() 
    { 
     locked = false; 

     lock->unlock(); 
    } 

    T *operator ->() const 
    { 
     _ASSERT(locked); 
     if (!locked) 
      throw new std::exception("Synchronization error ! Object lock is already released !"); 

     return this->tobject; 
    } 

    operator T *() const 
    { 
     _ASSERT(locked); 
     if (!locked) 
      throw new std::exception("Synchronization error ! Object lock is already released !"); 

     return this->tobject; 
    } 
}; 

感謝您提前發表任何意見和建議。

法提赫

+0

您不需要在縮進的代碼塊中編寫'&gt'(它總是缺少';')。只要寫'>'。 – 2010-12-15 12:40:09

+0

如果您可以獲得副本,請查看Alexandrescu的* Modern C++ Design *。 – 2010-12-15 13:13:19

回答

2

你有沒有聽說過The Law of Demeter

有類似的提醒(從薩特我認爲):不要共享引用您的內部

兩者都旨在避免耦合,因爲通過共享引用到內部,這意味着你的公共接口泄漏了一個實現細節。

既然這說,你的界面不起作用。

的問題是,你有鎖定了代理,而不是對象:我仍然可以通過多條路徑訪問:

  • Foo,無需互斥 - > oups?
  • 來自兩個不同locked_object - >這似乎並不是故意的...

更重要的是,通常情況下,您不能鎖定對象的單個部分,因爲那樣您就無法爲整個對象提供事務性語義。

+0

德米特/ L.K.P法律+1 – 2010-12-15 17:22:35

1

這不是特別安全。沒有從把事情停止用戶不按順序:

locked_object<T> o(...); 
T* t = o.get(); 
o.unlock(); 
t->foo(); // Bad! 

當然,很容易看出爲什麼上面的代碼是壞的,但真正的代碼更加複雜,有很多種方法,其中三分球都挺在鎖定釋放後更難以確定。

2

您的設計對用戶負責,以確保對象在正確的時間鎖定和解鎖。即使你鎖定的對象沒有進行錯誤檢查,它也不會覆蓋所有的基礎(比如在完成時忘記釋放對象)

因此可以說你有不安全的對象TData。您將此文件包裝在Foo中,而不是Foo返回TData的指針,重新實現TDataFoo中的所有公共方法,但使用鎖定和解鎖。

這與pImpl模式非常相似,除非您的界面在調用實施之前添加了鎖。這樣用戶就知道該對象是線程安全的,不需要擔心同步。

2

這是多線程的核心問題,您不能要求客戶端代碼以線程安全的方式使用您的對象。你也不能真正做任何事情來幫助客戶端代碼陷入成功之坑,它必須自己照顧鎖定。把你的代碼工作在最不可能正確的人身上。

您可以通過從您的存取器中返回對象的副本來簡化操作。這是線程安全的,只有一個線程擁有一個副本。您可能應該使該副本的類型不可變,以重新執行,以便修改該對象不會產生期望的結果。無法解決的副作用可能會嚴重影響,因爲這個副本根據定義是陳舊的。這些是可能造成更多傷害而不是更好的創可貼。

記錄方法的填充情況,以便客戶端程序員知道該怎麼做。