Действительно ли std::deque потокобезопасен?
Я знаю, что говорится в книгах о том, что std::deque умеренно безопасен для потоков, но мой опыт доказывает обратное. Я использую VS 2010. Есть по крайней мере два потока (может быть N потоков, но добавление потоков только делает проблему быстрее), каждый из которых выполняет один и тот же код. Каждый поток содержит один и тот же код, однако указатель на уникальный экземпляр структуры, содержащей deque, передается каждому потоку, поэтому теоретически у каждого из них есть своя собственная deque для работы. Однако в разное время я получаю ошибки, когда потоки пытаются получить доступ к deque (всегда чтение). Deque определяется примерно так:
struct A
{
deque<TAS*> dqTas;
}
TAS - указатель на другую структуру.
Структура А создается как
A* Aptr = new A;
Структура TAS также создается таким же образом
TAS* pTas = new TAS
Ошибки характеризуются:
1) они случаются в случайное время в коде. Потоки могут работать в течение нескольких минут, обрабатывая чтение / запись в очередь до появления ошибки.
2) Чем больше потоков, тем быстрее возникает проблема. Проблема никогда не происходит с 1 потоком.
3) Сообщения об ошибках различаются, говоря, что либо невозможно разыменовать деку, либо индекс находится вне области видимости. Если deque не может разыскивать ошибку, то причина, по которой при проверке данных полностью не обнаруживается. Все выглядит по порядку, указатели, существующие данные в deque и т. Д. Если проблема заключается в том, что индекс выходит за пределы диапазона, то каким-то образом один или несколько элементов данных (из нескольких сотен) внезапно повреждаются в самой deque.
Я удалил все удаления из каждого пути рабочего процесса, чтобы непреднамеренное удаление памяти не могло быть проблемой.
Единственное, что может вызвать это - глобальный счетчик или указатель в коде std::deque. Символ этих ошибок указывает на источник столкновения потока. Я даже проверил, что адреса каждого экземпляра структуры разные. Теоретически, вероятность коллизий должна быть нулевой, поскольку каждый поток имеет свою собственную копию deque. Единственный способ, которым это происходит с этой настройкой, - это если в коде std::deque есть глобальный ptr или счетчик.
Кто-нибудь еще имел этот опыт? Будут ли функции повышения дек в лучшем случае работать лучше в таком сценарии?
Если вам интересно, это код, который 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 было симптомом проблемы. Проблема была вызвана некоторыми старыми статическими переменными, объявленными локально в функции класса, который был создан потоком. Эти переменные были перезаписаны адресами других объектов, сохраненными в той же самой папке в другом потоке. Я просто избавился от них, и все стало работать как положено. Как бы это ни звучало элементарно, урок, который нужно запомнить, заключается в том, что статические переменные по сути являются глобальными переменными (даже если они определены локально внутри функции) в разных потоках. Вероятно, лучше всего избегать их в любом коде, который входит в потоки, которые могут запускать несколько экземпляров одного и того же кода, если не очень ясно, почему и как они используются.
1 ответ
Обычно ни один контейнер не является "потокобезопасным". Это просто контейнер. Я бы порекомендовал вам сделать это самостоятельно. Создание объекта std::lock_guard с использованием std::mutex в стеке сделает ваш код потокобезопасным. Надеюсь, это поможет: ниже пример кода:
std::mutex lockMutex;
std::lock_guard<std::mutex> lock(lockMutex);