2015-03-14 3 views
1

У меня есть стандартный вектор и несколько тем. Я использую следующий код для блокировки, когда это необходимо:STL vector многопоточность

boost::mutex::scoped_lock lock(mutex); 

Это работает должным образом, приложение работает без каких-либо проблем, но теперь я создал небольшой класс для вектора, чтобы сделать свою жизнь проще:

template <class T> class FVector 
{ 
private: 
    std::vector<T>   standard_vector; 
    mutable boost::mutex mutex; 

public: 
    typedef typename std::vector<T>::iterator iterator; 
    typedef typename std::vector<T>::size_type size_type; 

    FVector(void) 
    { 

    } 

    iterator begin(void) 
    { 
     boost::mutex::scoped_lock lock(mutex); 
     return standard_vector.begin(); 
    } 

    iterator end(void) 
    { 
     boost::mutex::scoped_lock lock(mutex); 
     return standard_vector.end(); 
    } 

    void push_back(T & item) 
    { 
     boost::mutex::scoped_lock lock(mutex); 
     standard_vector.push_back(item); 
    } 

    void erase(iterator it) 
    { 
     boost::mutex::scoped_lock lock(mutex); 
     standard_vector.erase(it); 
    } 
}; 

Но, к сожалению, это не работает. Я просто получаю xx.exe вызвал точку останова. исключение, что означает, что что-то не так с замками и несколькими потоками, которые пытаются писать и читать в одно и то же время.

Я использую следующий код для тестирования:

#include <Windows.h> 
#include <process.h> 
#include "thread_safe_vector.h" 

struct TValue 
{ 
    int value; 
}; 

FVector<TValue> vec_Safe; 

boost::mutex testMutex; 

void thread2(void* pArg) 
{ 
    while (true) 
    { 
     //boost::mutex::scoped_lock lock(testMutex); 

     for (FVector<TValue>::iterator it = vec_Safe.begin(); it != vec_Safe.end(); it++) 
     { 
      if (it->value == 5) 
      { 
       vec_Safe.erase(it); 
       break; 
      } 
     } 
    } 
} 

void thread1(void* pArg) 
{ 
    while (true) 
    { 
     TValue value; 
     value.value = 5; 

     //boost::mutex::scoped_lock lock(testMutex); 

     vec_Safe.push_back(value); 
    } 
} 

void main(void) 
{ 
    HANDLE hThreads[50]; 

    for (size_t i = 0; i < 50; i++) 
    { 
     hThreads[i] = (HANDLE)_beginthread(i % 2 == 0 ? thread1 : thread2, NULL, NULL); 
    } 

    system("pause"); 

    for (size_t i = 0; i < 50; i++) 
    { 
     TerminateThread(hThreads[i], 0); 
    } 
} 

Я абсолютно из идей, я попытался выяснить проблему в течение нескольких часов ... Есть ли что-нибудь, что я делаю неправильно ?

ответ

3

Перемещая блокировки вниз на нижний уровень абстракции, вы вводили данные гонки.

for (FVector<TValue>::iterator it = vec_Safe.begin(); it != vec_Safe.end(); it++) 

Вот как begin() и end() выполнять под замком, но сравнение не делает. Гораздо хуже:

{ 
     if (it->value == 5) 

dereferences устаревший итератор это какая-то другая сторона изменила его.

Еще более принципиально любая операция перераспределения может аннулировать все итераторы одним махом.

Вы всегда будете иметь, чтобы зафиксировать для всего цикла. Если тело цикла принимает значительное время, вы можете скопировать элементы под замком и обработать их потом.

Однако в таких случаях более типично использовать (lockfree) очередь и объединить элементы из общей очереди в локальную для большей эффективности.

+0

Благодарим вас за быстрый ответ, так что, похоже, нет возможности делать блокировки только в классе «FVector»? Я хотел, чтобы я не добавлял блокировки в каждый цикл. – CsOkemf

+0

@ CsOkemf не так, как вы думали. Вы можете проектировать API-интерфейсы, которые возвращают внедряемые блокировки из их операций, но такие проекты не так много ясны в моем опыте. – sehe

+0

Хм, спасибо. Может быть, есть что-то, что вы рекомендуете использовать? – CsOkemf