2009-11-20 51 views
1

叫說我有一類A一定保障私有函數只能從鎖定狀態

class A { 
public: 
    A(); 

    void fetch_data() { return 1; } 
    void write_x_data() { 
    // lock this instance of A 
    private_function1_which_assumes_locked(); 
    private_function2_which_assumes_locked(); 
    // unlock this instance of A 
    } 
    void write_y_data() { 
    // lock this instance of A 
    private_function1_which_assumes_locked(); 
    // unlock this instance of A 
    } 
private: 
    void private_function1_which_assumes_locked(); 
    void private_function2_which_assumes_locked(); 
}; 

我想保證,除非A被鎖定private_function*_which_assumed_locked()永遠不能被調用。

什麼是實現這一目標的最佳方式是什麼?我有5個左右的需要鎖定的公共功能。這些功能永遠不會互相呼應,所以我並不擔心這些會導致死鎖。綜合起來,這5個公共函數調用了大約15個不同的私有函數,它們需要假設對象處於鎖定狀態。顯然,我不能鎖定私有函數,因爲我會陷入僵局。

隨意提供合理的高層次抽象的答案,假設Mutexes和Scopeguards的存在。

在Python我可能會做一些像decorators

class locked_method(object): 
    def __init__(self, f): 
     self.f = f 

    def __call__(self): 
     # Do whatever is needed to lock 
     self.f() 
     # Do whatever is needed to unlock 

class checklocked(object): 
    def __init__(self, f): 
     self.f = f 

    def __call__(self): 
     # check locked, if not, don't call self.f(), 
     # and yell at me loudly for screwing up. 
     self.f() 

@locked_method 
def func1(): 
    __function_which_assumes_locked() 

@checklocked 
def __function_which_assumes_locked(): 
    pass 

注:我沒有在Python的方式做了很多工作,可以隨意評論,如果我的Python是錯誤/愚蠢,但這個問題的重點更多的是在C++中完成這種事情的最好方式,所以希望Python提供的內容足以讓你知道我想做什麼。

+0

遞歸(可重入)互斥體呢? – Arkadiy 2009-11-20 16:15:48

+0

@Arkadiy - 有趣的想法。碰巧我沒有訪問遞歸互斥類,但如果這是唯一的方法去,那麼我想我必須找到一個。任何其他想法? – 2009-11-20 16:21:38

回答

4

你可以使用一個儲物櫃類,並且需要一個存在,以調用私有函數:

class A 
{ 
public: 
    void write() 
    { 
     Lock l(this); 
     write(l); 
    } 

private: 
    void lock(); 
    void unlock(); 
    void write(const Lock &); 

    class Lock 
    { 
    public: 
     explicit Lock(A *a) : parent(a) {parent->lock();} 
     ~Lock() {parent->unlock();} 
    private: 
     A *parent; 
    }; 
}; 
+0

+1 - 這看起來像迄今爲止最好的建議,我會讓你知道我是怎麼做的! – 2009-11-20 16:35:33

+1

由於寫了一個更衣室類,我建議使用boost的很好的鎖類。 – 2009-11-20 21:38:35

+0

@Caspin:是的,如果你正在使用Boost.Thread,那就這樣做。 – 2009-11-20 23:58:31

0

您可以使用「#定義」來創建一個類似的效果:

#define CheckIfLocked(func, criticalSection) \ 
    // here you can enter the critical section 
    func \ 
    // exit critical section 
0

您可以使用C++ locking mechanisms之一。

使用其中一個「嘗試」功能作爲每個私人功能的第一步 - 這些功能只是檢查鎖是否鎖定。如果未鎖定,則拋出異常。

P.S.確保私鑰函數只能從鎖定狀態調用 -time ...這可能會在Haskell中成爲可能:)

+0

在C++中編譯時檢查是完全可能的:查看我的答案。 – 2009-11-20 16:30:18

+0

你是對的!好的回答:) – Danra 2009-11-20 19:17:26

0

在每個公共開始時鎖定對象A一次功能並在處理完成時釋放鎖。公共職能是處理開始的入口點。你將不必擔心在每個私有函數鎖定。

看一看RAII鎖定/解鎖對象(或使用像boost::mutex::scoped_lock)。

+0

是的,但是如果另一個不執行鎖定的函數調用該私有函數呢?這是問題的核心。 – 2009-11-20 16:52:49

+0

只要處理以公共函數調用開始,就不會成爲問題。由於對象從一開始就被鎖定,所以私有函數可以使用其他私有函數。如果不是首先通過公共職能,你將如何訪問私有函數? – Houlalala 2009-11-20 17:04:07

+0

是的,但是如果一個公共函數沒有將調用鎖定到一個假定該對象被鎖定的私有函數中呢?我如何在編譯時保證不會發生這種情況? – 2009-11-20 17:09:58

0

鎖()函數鎖定互斥並把線程id成成員變量。 unlock()將成員變量設置爲0並解鎖互斥鎖

在您的私有函數中檢查該線程ID是否屬於您自己的線程,並且如果不是這樣,則會大聲抱怨。

2

這讓我想起一個Dr. Dobb's article by Andrei Alexandrescu的我。

這個想法是使用這樣一個事實,就像const成員函數不能調用非const成員函數一樣,volatile成員不能調用非成員函數。然後,他使用volatile來「標記」線程安全函數,而非易失性函數是「不安全」的。因此,這不能編譯:

class Foo 
{ 
public: 
    // Not the "volatile" 
    int External() volatile 
    { 
     return this->Internal(); 
    } 

private: 

    // Not volatile 
    int Internal(); 
}; 

因爲this不能從Foo volatile*轉換爲Foo*。演員是必要的。這是由輔助班作出的,也扮演RAII鎖持有人的角色。所以Foo變成:

class Foo 
{ 
public: 
    int External() volatile 
    { 
     Lock self(this); 
     return self->Internal(); 
    } 

private: 

    class Lock{ 
    public: 
     explicit Lock(volatile Foo* Self) 
      : m_Mutex.Aquire(), 
       m_Self(const_cast<Foo*>(Self)) 
     {} 

     ~Lock() 
     { 
      m_Mutex.Release(); 
     } 

     Foo* operator->(){ 
      return m_Self; 
     } 

    private: 
     SomeMutexType m_Mutex; 
     Foo* m_Self; 
    }; 


    int Internal(); 
}; 
+0

這真是一個黑客。但非常聰明! +1 – Arkadiy 2009-11-20 20:43:26