2012-01-14 6 views
1

Как std::vector не является потокобезопасным, я пытался построить очень простой инкапсуляция вокруг него, что делает его потокобезопасным.Release boost :: mutex from destructor

Это работает довольно хорошо, но есть одна небольшая проблема. Когда экземпляр класса разрушается, а другой поток все еще пытается прочитать данные из него, нить продолжает вешать навсегда в 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(); 
} 

Edit я должен изменить мое определение. Контейнер, очевидно, не является потокобезопасным, как указано выше. Он не должен этого делать - даже если номенклатура вводит в заблуждение. Уверен, что вы можете делать с ним все, что не является потокобезопасным! Но только один поток записывает в контейнер, 2 или 3 читает его. Он работает хорошо, пока я не попытаюсь остановить процесс. Я должен сказать, что монитор был бы лучше. Но время истекает, и я не могу изменить это до тех пор.

Любая идея оценивается! Спасибо и приветствую.

+3

Я думаю, что у вас есть поточные вопросы на более высокий уровне. Похоже, что для потока 1 кажется ошибкой думать, что законно уничтожать объект, в который есть ссылка 2, и полагает, что он может читать из него. Что делать, если потоку 1 действительно удалось полностью уничтожить объект, а _then_ thread 2 попытался прочитать из него? –

+1

Не будет ли конструктор копирования также блокировать * другой * вектор? –

+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

Смежные вопросы