STL-вский vector и снихронизация потоков
От: RealBobEx  
Дата: 04.08.05 09:23
Оценка:
Привет всем!
Читал я дискуссию по поводу потокобезопасности vector-а и решил привести реальный пример, когда vector съезжает.
Ситауация такая:
Есть два потока, в одном кусок кода который пишет в вектор выглядит так:
       singleLock.Lock();
       m_Vector.push_back(/* некое значение */);
       singleLock.Unlock();

sinleLock объявлен до начала цикла так:
        CMyApp* pApp = (CMyApp*) AfxGetApp();
        CSingleLock singleLock(&pApp->m_critSection);

Критикал секшн объявлена в объекте приложения (для простоты) так:
public:
    CCriticalSection m_critSection;

Вектор объявлен во втором потоке:
    vector<pair<basic_string<T>, LONG> > m_Vector;

В первый поток передается ссылка на него (или указатель).
Во втором потоке должен до завершения (т.е. до достижения vector.end()) отработать тестовый цикл:
    vector<pair<basic_string<T>, LONG> >::iterator I;
    CMyApp* pApp = (CMyApp*) AfxGetApp();
    CSingleLock singleLock(&pApp->m_critSection);

    long i = 0;
    for(I = m_Vector.begin(); I < m_Vector.end(); I++)
    {
        singleLock.Lock();
        TRACE("Index = %i\n", i);
        i++;
        singleLock.Unlock();
    }

ЧТО ЖЕ происходит НА САМОМ ДЕЛЕ?
Итератор I указывает на несуществующее значение и при обращении (*m_Vector).first получаем ошибку и МАЛО ТОГО, цикл спокойно крутится до значений i гораздо больших чем m_Vector.size() — 1.
ВСЕ ЭТО РАБОТАЕТ только в том случае, если написать цикл иначе:
    while(i < m_Vector.size())
    {
        //повторяем то же самое что и выше, только для поучения значений используем оператор
        //m_Vector[i].first и m_Vector[i].second.
    }

Кто-нибудь понимает почему съезжает итератор?
(Ооооочееннннь не хочется переделывать все потоки содержащие такой цикл (больно их много).)
Как все-таки заставить эту фигню правильно работать?
Re: STL-вский vector и снихронизация потоков
От: rus blood Россия  
Дата: 04.08.05 11:02
Оценка:
Здравствуйте, RealBobEx, Вы писали:

Векто съезжает из-за того, что при добавлении айтемов в вектор тот может производит реаллокацию своего буфера. При этом полученные ранее итераторы становятся невалидными.

У тебя один поток добавляет айтем, другой — использует итератор, который после добавления становится невалидным.
Имею скафандр — готов путешествовать!
Re: STL-вский vector и снихронизация потоков
От: Psyton Россия  
Дата: 04.08.05 11:43
Оценка:
А никто и не говорил что stl::vector thread-safe, сами разработчики утверждают как раз обратное..
Re[2]: STL-вский vector и снихронизация потоков
От: RealBobEx  
Дата: 04.08.05 12:08
Оценка:
Здравствуйте, rus blood, Вы писали:

RB>Здравствуйте, RealBobEx, Вы писали:


RB>Векто съезжает из-за того, что при добавлении айтемов в вектор тот может производит реаллокацию своего буфера. При этом полученные ранее итераторы становятся невалидными.


RB>У тебя один поток добавляет айтем, другой — использует итератор, который после добавления становится невалидным.


Хммм... В таком случае должно помогать простое обновление значений итераторов, например так:
    singleLock.Lock();
    I = m_Vector.begin();
    End = m_Vector.end();
    singleLock.Unlock();

    long i = 0;
    for(I; I < End; )
    {
        singleLock.Lock();
        //Используем I, т.к. теперь он валидный.
        i++;
        I++;
        End = m_Vector.end();
        singleLock.Unlock();
    }

Однако это помогает только не выйти за верхнюю границу, но правильные значения все равно не получаются.
Re[3]: STL-вский vector и снихронизация потоков
От: rus blood Россия  
Дата: 04.08.05 12:12
Оценка:
Здравствуйте, RealBobEx, Вы писали:


RBE>Хммм... В таком случае должно помогать простое обновление значений итераторов, например так:

RBE>
RBE>    singleLock.Lock();
RBE>    I = m_Vector.begin();
RBE>    End = m_Vector.end();
RBE>    singleLock.Unlock();

RBE>    long i = 0;
RBE>    for(I; I < End; )
RBE>    {
RBE>        singleLock.Lock();
RBE>        //Используем I, т.к. теперь он валидный. <- да не фига он не валидный. Про него речь-то !!!
RBE>        i++;
RBE>        I++;
RBE>        End = m_Vector.end();
RBE>        singleLock.Unlock();
RBE>    }    
RBE>


Итератор I остается старым -> инвалидируется...
Имею скафандр — готов путешествовать!
Re[4]: STL-вский vector и снихронизация потоков
От: realbob  
Дата: 04.08.05 18:45
Оценка:
Удалено избыточное цитирование. — SchweinDeBurg

Здравствуйте, rus blood, Вы писали:

RB>Итератор I остается старым -> инвалидируется...


Т.е. Вы утверждаете, что синхронизация в данном случае невозможна?
Так же думает и Psyton.
Ну а зачем тогда объекты синхронизации? Они — то как раз и имеют цель предотвратить
доступ к вектору когда идет запись в него, иначе говоря предназначенны сделать использоване обычной коллекции потоко-безопасным.
Если бы Ваше предполжение о перераспределении памяти было верно, то был бы нерабочим элементарный код:
       vector<int> v;
       vector<int>::iterator I;
       for(i = 0; i < 10000; i++)
            v.push_back(1);
       for(I = v.begin(); I < v.end(); I++)
            //используем I.

       for(i = 0; i < 10000; i++)
            v.push_back(2); //Память перераспередлилась.

       for(I = v.begin(); I < v.end(); I++)
            //используем I. <<тут была бы ошибка. А это, согласитесь, мягко говоря не так.

Я предположил другую причину.
И нашел решение которое реально заработало в моем коде.
Не изобретай лишних сущностей без крайней необходимости!
Re[5]: STL-вский vector и снихронизация потоков
От: rus blood Россия  
Дата: 05.08.05 05:14
Оценка:
Здравствуйте, realbob, Вы писали:

R>
R>       vector<int> v;
R>       vector<int>::iterator I;
R>       for(i = 0; i < 10000; i++)
R>            v.push_back(1);
R>       for(I = v.begin(); I < v.end(); I++)
R>            //используем I.

R>       for(i = 0; i < 10000; i++)
R>            v.push_back(2); //Память перераспередлилась.

1. Тот I, который был в первом цикле, стал невалидным.

R>       for(I = v.begin(); I < v.end(); I++)
R>            //используем I. <<тут была бы ошибка. А это, согласитесь, мягко говоря не так.

2. Тут не будет ошибки. Ведь Вы же проинициализировали I заново, вызвав begin() в начале цикла!!!

R>


Что-то Вы похоже запутались в итераторах. Сначала пишете неверную конструкцию, и считаете, что она будет работать верно. Потом Вы пишете верный код и ждете ошибку...

Попробуйте для сравнения вот так —

       vector<int> v;
       vector<int>::iterator I;
       for(i = 0; i < 10000; i++)
            v.push_back(1);
       for(I = v.begin(); I < v.end(); I++)
            //используем I.

       I = v.begin(); // <- выставляем I в начало вектора перед повторным распределением...

       for(i = 0; i < 10000; i++)
            v.push_back(2); //Память перераспередлилась.

       for(; I < v.end(); I++) // <- начинаем цикл с текущего значения итератора...
            //используем I. <<тут была бы ошибка. А это, согласитесь, мягко говоря не так.


и Вас почуствуете, что такое невалидный итератор.
Имею скафандр — готов путешествовать!
[от модератора]
От: SchweinDeBurg Россия https://zarezky.spb.ru/
Дата: 05.08.05 05:47
Оценка:
Убедительная просьба — следить за объемом цитирования.
[ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
- Искренне ваш, Поросенок Пафнутий
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.