2016-09-02 7 views
0

나는 std :: mutex를 최근에 사용 해왔고 멤버로 std :: mutex가있는 객체를 삭제할 때 패턴/디자인 안내서를 찾고있다. 이 문제는 뮤텍스 (모니터 함수, 임계 구역 등)를 사용하는 공용 함수로서 객체가 삭제 될 때 발생하며이 객체가 삭제 될 때 스레드가 여전히 뮤텍스를 기다리고있을 수 있습니다. std :: mutex 다른 스레드에 의해 잠겨있는 동안 정의되지 않은 동작이 삭제되어 문제가 발생합니다.회원 계정을 안전하게 삭제하는 방법 :: 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로 동시 메소드를 구현하십시오. 이 객체를 포함하는 다른 모든 객체가 삭제되고 뮤텍스가있는 모든 lamd가 처리되면이 객체가 자동으로 삭제됩니다.

이 코드는 나머지 코드에 몇 가지 제한이 있으므로이 방법이 마음에 들지 않습니다. WinRT의 경우이 메서드는 어떤 스레드가이 개체를 삭제할지 제어하지 않으므로 결과적으로 UI 스레드 오류가 발생할 수 있습니다.

+0

이것은 뮤텍스에만 해당되는 것은 아닙니다. 구성원이 아직 사용 중이면 개체를 삭제할 수 없습니다. – MSalters

+0

@MSalters 동시성이 더 어렵습니다. 단일 스레드 코드의 경우 소멸자 또는 멤버 함수를 동시에 실행할 수 없습니다. 그리고 개체가 더 이상 필요하지 않을 때 개체가 삭제되므로 참조 된 개체 또는 RAII를 사용하여 항상 안전하게 삭제할 수 있습니다 ... 순환 참조 등은 예외입니다. – legokangpalla

답변

1

개체 내의 데이터로 개체의 수명을 보호 할 수 없습니다.

외부 뮤텍스를 사용하여 개체를 보호 할 수 있습니다.

그래서 시작 :

locked<int> my_int = 7; 

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

다음, 물건 그것으로 std::unique_ptr<YourClass> :

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; 
}; 

사용 것 같습니다. 이렇게하면 사람들이 그것을 삭제할 수 있습니다.

마지막으로 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 = {}; 
}; 

잠금이되지 위젯 주위, 위젯에 대한 포인터 주위를. 위젯에 대한 포인터에 대한 포인터가 공유됩니다.

코드는 테스트하지 않았지만 비슷한 디자인이 적용되었습니다.