2012-01-14 21 views
1

std::vectorはスレッドセーフではありませんでしたので、私はを非常に簡単に構築して、スレッドセーフであるようにカプセル化しました。デストラクタからboost :: mutexをリリースしました

これは非常にうまくいきますが、少し問題があります。 クラスのインスタンスが破棄されていて、別のスレッドが引き続きデータを読み込もうとしているとき、スレッドは永遠にハングし続けます。boost::mutex::scoped_lock lock(m_mutex);

どうすればこの問題を解決できますか?最高ののは、スレッドにぶら下がっているスレッドが実行を継続できるように、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(); 
} 

編集私は私の定義を変更する必要があります。以前に述べたように、コンテナは明らかにスレッドセーフではありません。たとえその命名法が誤解を招くものであっても、そうすることは想定されていません。もちろん、スレッドセーフではないことを行うことができます。しかし、1つのスレッドだけがコンテナに書き込みます.2つまたは3つのスレッドから読み込みます。プロセスを停止しようとするまでうまく動作します。私はモニターが良いと述べなければなりません。しかし、時間がなくなり、私はこれまでこれを変更することはできません。

ご了承ください。ありがとうございました。

+3

私はあなたがより高いレベルでスレッドの問題を抱えていると思います。スレッド1がスレッド2が参照を持っているオブジェクトを破棄するのが正当だと考えることはエラーのようであり、スレッド2から参照できると信じています。スレッド1が実際にオブジェクトを完全に破壊し、スレッド2がそこから読み取ろうとした場合はどうなりますか? –

+1

コピーコンストラクタも* other *ベクタをロックする必要はありませんか? –

+0

コピー代入演算子の実装についても興味があります。劇的に間違ってしまうのは簡単でしょう。 –

答えて

1

EDIT:より完全な例に更新されました。

他の人は、あなたの「スレッドの安全性」の欠陥を指摘しています。私はあなたの質問に答えようとします。

あなたがしたことを行う適切な唯一の方法は、ベクター自体を破壊しようとする前に、すべてのスレッドがシャットダウンされていることを確認することです。

私が使用した一般的な方法は、単に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

私はQueueクラスで読み込みをブロックしているので、取り消しが必要です。クラスの "ブロックしてデータを待つ"機能はありません。これは実際のエラーが、あなたがアクセスしようとしていたミューテックスがロックの前/中に破壊されたと仮定しています。これが起こる唯一の方法は、ベクターがスレッドの前*で破棄された場合です。上記の例では、キャンセルの例があります。また、mutexが耐用年数を過ぎる前に破壊することはできません。 – lefticus

関連する問題