2012-01-14 2 views
1

std::vector은 스레드로부터 안전하지 않으므로 스레드 주위를 둘러싼이라는 매우 간단한 캡슐화로 을 만들려고했습니다.boost :: mutex from descructor

이 아주 잘 작동하지만 하나의 작은 문제가있다. 클래스의 인스턴스가 파괴되고 다른 스레드가 여전히 데이터를 읽으려고 할 때 스레드는 계속해서 데이터를 계속 읽습니다. boost::mutex::scoped_lock lock(m_mutex);

어떻게 해결할 수 있습니까? 가장 좋은 점은 뮤텍스에 걸려있는 스레드가 계속 실행될 수 있도록 뮤텍스를 잠금 해제하는 것입니다. 지금까지는 필요하지 않았기 때문에 소멸자를 정의하지 않았습니다. 여기

내 코드입니다. 여기에 표시된 것보다 더 많은 메소드가 있지만, 단순화되었습니다.

template<class T> 
class SafeVector 
{ 
    public: 
    SafeVector(); 
    SafeVector(const SafeVector<T>& other); 

    unsigned int size() const; 
    bool empty() const; 

    void clear(); 
    T& operator[] (const unsigned int& n); 

    T& front(); 
    T& back(); 

    void push_back(const T& val); 
    T pop_back(); 

    void erase(int i); 

    typename std::vector<T>::const_iterator begin() const; 
    typename std::vector<T>::const_iterator end() const; 

    const SafeVector<T>& operator= (const SafeVector<T>& other); 

    protected: 
    mutable boost::mutex m_mutex; 
    std::vector<T> m_vector; 

}; 

template<class T> 
SafeVector<T>::SafeVector() 
{ 

} 

template<class T> 
SafeVector<T>::SafeVector(const SafeVector<T>& other) 
{ 
    this->m_vector = other.m_vector; 
} 

template<class T> 
unsigned int SafeVector<T>::size() const 
{ 
    boost::mutex::scoped_lock lock(m_mutex); 
    return this->m_vector.size(); 
} 

template<class T> 
bool SafeVector<T>::empty() const 
{ 
    boost::mutex::scoped_lock lock(m_mutex); 
    return this->m_vector.empty(); 
} 

template<class T> 
void SafeVector<T>::clear() 
{ 
    boost::mutex::scoped_lock lock(m_mutex); 
    return this->m_vector.clear(); 
} 

template<class T> 
T& SafeVector<T>::operator[] (const unsigned int& n) 
{ 
    boost::mutex::scoped_lock lock(m_mutex); 
    return (this->m_vector)[n]; 
} 

template<class T> 
T& SafeVector<T>::front() 
{ 
    boost::mutex::scoped_lock lock(m_mutex); 
    return this->m_vector.front(); 
} 

template<class T> 
T& SafeVector<T>::back() 
{ 
    boost::mutex::scoped_lock lock(m_mutex); 
    return this->m_vector.back(); 
} 

template<class T> 
void SafeVector<T>::push_back(const T& val) 
{ 
    boost::mutex::scoped_lock lock(m_mutex); 
    return this->m_vector.push_back(val); 
} 

template<class T> 
T SafeVector<T>::pop_back() 
{ 
    boost::mutex::scoped_lock lock(m_mutex); 
    T back = m_vector.back(); 
    m_vector.pop_back(); 
    return back; 
} 

template<class T> 
void SafeVector<T>::erase(int i) 
{ 
    boost::mutex::scoped_lock lock(m_mutex); 
    this->m_vector.erase(m_vector.begin() + i); 
} 

template<class T> 
typename std::vector<T>::const_iterator SafeVector<T>::begin() const 
{ 
    return m_vector.begin(); 
} 

template<class T> 
typename std::vector<T>::const_iterator SafeVector<T>::end() const 
{ 
    return m_vector.end(); 
} 

편집 나는 나의 정의를 변경해야합니다. 이전에 언급 한 것처럼 컨테이너는 스레드로부터 안전하지 않습니다. 명칭이 오도 된 경우에도 그렇게 할 수는 없습니다. 물론 스레드로부터 안전하지 않은 작업을 수행 할 수 있습니다. 그러나 단 하나의 쓰레드 만이 컨테이너에 쓰여지고, 2 ~ 3 개의 쓰레드는 컨테이너에서 읽는다. 프로세스를 멈추기 전까지는 제대로 작동합니다. 나는 모니터가 더 좋았을 것이라고 말해야한다. 그러나 시간이 없어서 그때까지는 이것을 바꿀 수 없습니다.

어떤 아이디어라도 만족합니다! 고마워, 안부.

+3

높은 수준의 스레딩 문제가 있다고 생각합니다. 쓰레드 1이 쓰레드 2가 참조를 가지고 있고 그것을 읽을 수 있다고 믿는 객체를 파기하는 것이 합법적이라고 생각하는 것은 오류처럼 보입니다. 쓰레드 1이 실제로 객체를 완전히 없애고 _then_ 쓰레드 2가 객체를 읽으려고했다면 어떻게 될까요? –

+1

복사 생성자도 * other * 벡터를 잠글 수 있습니까? –

+0

복사 할당 연산자의 구현을 보는 것도 흥미로울 것입니다. 그것은 그것을 극적으로 잘못 이해하기 쉽습니다. –

답변

1

편집 : 업데이트는보다 완벽한 예가 될 수 있습니다.

다른 사람은 당신과 결함 지적 "스레드 안전;" 나는 너의 질문에 대답하려고 노력할 것이다.

당신이 착수 한 일을 할 수있는 유일한 적절한 방법은 당신이 시도하기 전에 당신의 모든 스레드가되었습니다 종료하고 벡터 자체를 파괴하는 것입니다.

내가 사용하고 일반적인 방법은 간단하게 생성과 소멸의 순서를 정의하는 RAII을 사용하는 것입니다.

void doSomethingWithVector(SafeVector &t_vec) 
{ 
    while (!boost::this_thread::interruption_requested()) 
    { 
    //operate on t_vec 
    } 
} 

class MyClassThatUsesThreadsAndStuff 
{ 
    public: 
    MyClassThatUsesThreadsAndStuff() 
     : m_thread1(&doSomethingWithVector, boost::ref(m_vector)), 
     m_thread2(&doSomethingWithVector, boost::ref(m_vector)) 
    { 
     // RAII guarantees that the vector is created before the threads 
    } 

    ~MyClassThatUsesThreadsAndStuff() 
    { 
     m_thread1.interrupt(); 
     m_thread2.interrupt(); 
     m_thread1.join(); 
     m_thread2.join(); 
     // RAII guarantees that vector is freed after the threads are freed 
    } 

    private: 
    SafeVector m_vector; 
    boost::thread m_thread1; 
    boost::thread m_thread2; 
}; 

여러 독자와 작가를 허용하는보다 완전한 스레드 안전 데이터 구조를 찾고 있다면, 내가 부스트를 사용하여 쓴 큐를 확인해 주시기 다시 잠시 스레드.

http://code.google.com/p/crategameengine/source/browse/trunk/include/mvc/queue.hpp

+0

. RAII에 대해 알고 있습니다. 여기에있는 "문제"는 조인이 항상 돌아 오지 않는다는 것입니다. 큐 구현을 간과하면 원칙이 제게 똑같은 것처럼 보입니다. 컨테이너에서 작업을 수행하는 모든 곳에서 scoped_lock을 넣는 것 - 내 구현은 다음과 같습니다. 취소 부분이 없습니다. – Atmocreations

+0

내 대기열 클래스에서 읽기가 차단되어 취소가 필요한 이유입니다. 클래스의 "블록 및 데이터 대기"기능은 없습니다. 이것은 실제 오류가 있다고 가정합니다. 액세스하려고했던 뮤텍스가 잠금 전/도중 파괴되었습니다. 이것이 일어날 수있는 유일한 방법은 벡터가 스레드 이전에 파괴 된 경우입니다. 위의 예제를 통해 이제는 취소 예제를 얻었을뿐만 아니라 뮤텍스의 수명이 다하기 전에 뮤텍스를 삭제할 수 없습니다. – lefticus