2016-09-02 74 views
0

我最近一直在使用std :: mutex,現在正在尋找一個刪除std :: mutex成員的對象的模式/設計指南。當一個對象作爲使用互斥體的公共函數(監視器函數,關鍵部分等)並且當這個對象被刪除時,它可能有線程仍然在等待互斥體。 std :: mutex在被其他線程鎖定時有未定義的行爲被刪除,所以問題就出現了。如何安全刪除成員std :: mutex?

我想知道什麼是在這種情況下通常可以接受的模式。或者,如果這被認爲是一種糟糕的編碼風格,則可以採用避免這種設計的方法,即)不要刪除其互斥方法仍在等待的對象。

實施例:

//a public method that uses mutex. 
IAsyncAction^ XInputBase::flushTask() 
{ 
    return create_async([this](){ 
     _monitorMutex.lock(); 
     if (_readyToFlush && !_noMoreFlush) { 
      //should flush only once, block additional flush signals. 
      _noMoreFlush = true; 
      _monitorMutex.unlock(); 
      //actually flush 
      concurrency::task<void> UITask = concurrency::create_task(Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, 
       ref new Windows::UI::Core::DispatchedHandler([=]() 
      { 
       onFlush(); 
      }))); 
     } 
     else { 
      _needToFlush = true; 
      _monitorMutex.unlock(); 
     }   
    }); 
} 

嘗試的解決方案: 析構等待互斥所有等待的線程都處理完後完全解鎖。我通過在互斥體外設置一個標誌來實現它,以便方法中的所有線程都正在退出或等待互斥體。然後,我已經鎖定std :: mutex上次在析構函數。鑑於std :: mutex是公平的,析構函數應該在其他等待線程處理後最後解鎖,然後銷燬該對象。

這不起作用,因爲std :: mutex在大多數平臺中不能保證公平。

工作解決方案類型: 將對象實現爲ref類或其他引用計數對象(智能指針)。然後將這個併發方法作爲一個lamda來實現,這個lamda擁有對象的強引用。一旦所有其他持有該對象的對象都被刪除並且所有帶有互斥鎖的lamdas都被處理,該對象將被自動刪除。

我不喜歡這種方法,因爲它對代碼的其餘部分提出了一些限制。對於WinRT,這種方法不能控制哪個線程刪除這個對象,因此UI線程可能發生錯誤。

+0

這不是特定於互斥體的。當任何成員仍在使用中時,您無法刪除該對象。 – MSalters

+0

@MSalters併發性更難。對於單個線程代碼,您不能同時運行析構函數或成員函數。而且,通過使用ref計數對象或RAII,您始終可以保證安全刪除,因爲只有在不再需要對象時纔會刪除該對象...除了循環引用等外 – legokangpalla

回答

1

您無法保護對象中數據的對象的生命週期。

您可以使用外部互斥鎖來保護對象。

所以用這個啓動:

template<class T> 
struct locked_view { 
    template<class F> 
    auto operator->*(F&& f) const 
    -> std::result_of_t< F(T const&) > 
    { 
    auto l = lock(); 
    return std::forward<F>(f)(*t); 
    } 
    locked_view(locked_view const&) = default; 
    locked_view& operator=(locked_view const&) = default; 
    locked_view()=default; 
    explicit operator bool() const { return m&&t; } 

    locked_view(std::mutex* min, T* tin):m(min), t(tin) {} 

private: 
    std::unique_lock<std::mutex> lock() const { 
    return std::unique_lock<std::mutex>(*m); 
    } 
    std::mutex* m; 
    T* t; 
}; 

template<class T> 
struct locked { 
    locked()=default; 
    locked(locked&& o): 
    t(o.move_from()) 
    {} 
    locked(locked const& o): 
    t(o.copy_from()) 
    {} 
    locked& operator=(locked&& o) { 
    auto tin = o.move_from(); 
    assign_to(std::move(tin)); 
    return *this; 
    } 
    locked& operator=(locked const& o) { 
    auto tin = o.copy_from(); 
    assign_to(std::move(tin)); 
    return *this; 
    } 

    template<class U, 
    std::enable_if_t<!std::is_same<std::decay_t<U>, locked>{}, int> =0 
    > 
    locked(U&& u): 
    t(std::forward<U>(u)) 
    {} 

    // stars of show: 
    locked_view<T const> read() const 
    { 
    return {&m, std::addressof(t)}; 
    } 
    locked_view<T> write() 
    { 
    return {&m, std::addressof(t)}; 
    } 

    T move_from() { 
    return write()->*[](T& tin){return std::move(tin);}; 
    } 
    T copy_from() const { 
    return read()->*[](T const& tin){return tin;}; 
    } 
    template<class U> 
    void assign_to(U&& u) { 
    write()->*[&](T& t){ t = std::forward<U>(u); }; 
    } 
private: 
    mutable std::mutex m; 
    T t; 
}; 

使用的樣子:

locked<int> my_int = 7; 

my_int.read()->*[](int x){ std::cout << x << '\n'; }; 

接下來,東西一std::unique_ptr<YourClass>進去。這將允許人們刪除它。

最後,分享一個std::shared_ptr<>它。

所以

template<class T> 
using shared_locked = std::shared_ptr< locked<T> >; 
template<class T> 
using shared_locked_ptr = shared_locked< std::unique_ptr<T> >; 

是你喜歡的類型。

要使用,他們這樣做:

shared_locked_ptr<widget> w; 
w->read()->*[&](auto& ptr) { 
    if (ptr) ptr->do_something(); 
}; 

,你去鎖定塊內檢查壽命。

線程安全的刪除對象:

w->write()->*[&](auto& ptr) { 
    ptr = {}; 
}; 

鎖是指向小部件周圍,不在身邊的小部件。指向該小部件的指針的指針是共享的。

代碼沒有經過測試,但類似的設計已經工作過。

相關問題