2016-05-13 358 views
9

我有兩個函數foobar,它們應該是相互排斥的,因爲它們對相同的數據進行操作。但foo複製了bar的很多代碼,所以我想重構foo來致電bar相互調用的互斥函數

這是一個問題,因爲然後我不能在兩個函數中使用單個互斥鎖,因爲foo會在調用bar時發生死鎖。所以,我只想要「互相排斥」,而不是「相互排斥」。

是否存在執行此操作的模式?我使用的是C++,如果我需要像shared_mutex這樣的東西,我可以使用C++ 14/boost。

+0

使用'的std :: mutex'üerhaps? –

+0

將互斥量作爲參數傳遞給共享代碼的重構部分。 –

+2

@πάνταῥεῖ:「üerhaps」聽起來像是你從度假帶回家的疾病,或者是金屬音樂會。 –

回答

20

定義的私人「解鎖」功能,並使用來自foobar

void bar_unlocked() 
{ 
    // assert that mx_ is locked 
    // real work 
} 

void bar() 
{ 
    std::lock_guard<std::mutex> lock(mx_); 
    bar_unlocked(); 
} 

void foo() 
{ 
    std::lock_guard<std::mutex> lock(mx_); 
    // stuff 
    bar_unlocked(); 
    // more stuff 
} 
+0

是的,比可怕的遞歸互斥更好的解決方案。 – SergeyA

+0

相當明顯(有相同的想法,但在這裏太遲了) – Walter

4

另一種方式 - 這具有的優點是可以證明的是,鎖已被採取:

void bar_impl(std::unique_lock<std::mutex> lock) 
{ 
    assert(lock.owns_lock()); 
    // real work 
} 

void bar() 
{ 
    bar_impl(std::unique_lock<std::mutex>(mx_)); 
} 

void foo() 
{ 
    // stuff 
    bar_impl(std::unique_lock<std::mutex>(mx_)); 
    // more stuff 
} 

理由:

std::mutex不是(由標準強制)是可移動的,但std::unique_lock<std::mutex>是。出於這個原因,我們可以將一個鎖移入一個被調用者,並將其返回給調用者(如果需要的話)。

這使我們能夠在呼叫鏈的每個階段證明鎖的所有權。

另外,一旦優化器參與,所有的鎖定移動都可能被優化掉。這給了我們兩全其美的好處 - 可證明的所有權和最佳表現。

一個更完整的例子:

#include <mutex> 
#include <cassert> 
#include <functional> 

struct actor 
{ 
    // 
    // public interface 
    // 

    // perform a simple synchronous action 
    void simple_action() 
    { 
    impl_simple_action(take_lock()); 
    } 

    /// perform an action either now or asynchronously in the future 
    /// hander() is called when the action is complete 
    /// handler is a latch - i.e. it will be called exactly once 
    /// @pre an existing handler must not be pending 
    void complex_action(std::function<void()> handler) 
    { 
    impl_complex_action(take_lock(), std::move(handler)); 
    } 

    private: 

    // 
    // private external interface (for callbacks) 
    // 
    void my_callback() 
    { 
    auto lock = take_lock(); 
    assert(!_condition_met); 
    _condition_met = true; 
    impl_condition_met(std::move(lock)); 
    } 


    // private interface 

    using mutex_type = std::mutex; 
    using lock_type = std::unique_lock<mutex_type>; 

    void impl_simple_action(const lock_type& lock) 
    { 
    // assert preconditions 
    assert(lock.owns_lock()); 
    // actions here 
    } 

    void impl_complex_action(lock_type my_lock, std::function<void()> handler) 
    { 
    _handler = std::move(handler); 
    if (_condition_met) 
    { 
     return impl_condition_met(std::move(my_lock)); 
    } 
    else { 
     // initiate some action that will result in my_callback() being called 
     // some time later 
    } 
    } 

    void impl_condition_met(lock_type lock) 
    { 
     assert(lock.owns_lock()); 
     assert(_condition_met); 
     if(_handler) 
     { 
     _condition_met = false; 
     auto copy = std::move(_handler); 
     // unlock here because the callback may call back into our public interface 
     lock.unlock(); 
     copy(); 
     } 
    } 



    auto take_lock() const -> lock_type 
    { 
    return lock_type(_mutex); 
    } 


    mutable mutex_type _mutex; 

    std::function<void()> _handler = {}; 
    bool _condition_met = false; 
}; 

void act(actor& a) 
{ 
    a.complex_action([&a]{ 
    // other stuff... 
    // note: calling another public interface function of a 
    // during a handler initiated by a 
    // the unlock() in impl_condition_met() makes this safe. 
    a.simple_action(); 
    }); 

} 
+0

更多的東西仍然在互斥鎖運行? '將鎖移入被調用者並將其返回給調用者'另一個選項是傳遞引用。 – jingyu9575