Я знаю, что книги говорят о том, что std::deque является умеренно потокобезопасным, но мой опыт доказывает обратное. Я использую VS 2010. Есть как минимум два потока (может быть N потоков, но добавление потоков только ускорит возникновение проблемы), каждый из которых выполняет один и тот же код. Каждый поток содержит один и тот же код, однако указатель на уникальный экземпляр структуры, содержащей двухстороннюю очередь, передается каждому потоку, поэтому теоретически у каждого из них есть своя двухсторонняя очередь для работы. Однако в разное время я получаю ошибки, когда потоки пытаются получить доступ к очереди (всегда чтение). Дека определяется примерно так:
struct A
{
deque<TAS*> dqTas;
}
TAS — это указатель на другую структуру.
Структура A создается как
A* Aptr = new A;
Структура TAS также создается таким же образом.
TAS* pTas = new TAS
Ошибки характеризуются:
1) они происходят в случайное время в коде. Потоки могут работать в течение нескольких минут, обрабатывая чтение/запись данных в очередь, прежде чем произойдет ошибка.
2) Чем больше потоков, тем быстрее проблема возникает. Проблема никогда не возникает с 1 потоком.
3) Сообщения об ошибках различаются, говоря, что двухсторонняя очередь не может быть разыменована или индекс выходит за рамки. Если дек не может разыменовать ошибку, то причина, по которой проверка данных полностью необнаружима. Все выглядит в порядке, указатели, существующие данные в двухсторонней очереди и т. д. Если проблема заключается в том, что индекс выходит за пределы диапазона, то каким-то образом один или несколько элементов данных (из нескольких сотен) внезапно повреждаются в самой двухсторонней очереди.
Я удалил все удаления из каждого пути рабочего процесса, чтобы непреднамеренное удаление памяти не могло быть проблемой.
Кажется, единственное, что могло вызвать это, — это глобальный счетчик или указатель в коде std::deque. Характер этих ошибок указывает на источник конфликта потоков. Я даже проверил, что адреса каждого экземпляра структуры разные. Теоретически вероятность коллизий должна быть нулевой, поскольку каждый поток имеет свою собственную копию очереди. Единственный способ, которым это происходит с этой настройкой, - это если в коде std::deque есть глобальный указатель или счетчик.
У кого-нибудь еще был такой опыт? Будут ли функции boost deque работать лучше в таком сценарии?
Если вам интересно, это код, который gpfs:
pTs->dqTas.push_front( pTb ); <<GPF happens after a write
#if defined (DEBUG)
long d2 = pTs->dqTas.size()-1;
if( d2 > 0 )
{
TASBAR* pDel2;
//pDel2 = pTs->dqTas[d2];
pDel2 = pTs->dqTas.at(d2); //<<GPF happens here
}
#endif
РАЗРЕШАЮЩАЯ СПОСОБНОСТЬ:
Спасибо всем за ваши комментарии. Проблема была решена, и она не имела ничего общего с контейнером deque. Случайное повреждение дека было симптомом проблемы. Проблема была вызвана некоторыми старыми статическими переменными, объявленными локально в функции класса, созданного потоком. Эти переменные перезаписывались адресами других объектов, сохраняемых в той же двухсторонней очереди в другом потоке. Я просто избавился от них, и все заработало, как ожидалось. Как бы элементарно это ни звучало, урок, который следует помнить, заключается в том, что статические переменные по существу являются глобальными переменными (даже если они определены локально внутри функции) между потоками. Вероятно, лучше вообще избегать их в любом коде, который входит в потоки, которые могут запускать несколько экземпляров одного и того же кода, если только не очень ясно, почему и как они используются.
deque
для каждого потока? - person i_am_jorf   schedule 10.04.2014.at()
там, чтобы ловить выход за пределы поля, а не незащищенный[]
, так что мы получили это, чтобы помочь. - person WhozCraig   schedule 10.04.2014