стандартные контейнеры как локальные переменные в многопоточном приложении - PullRequest
12 голосов
/ 01 марта 2012

Мне известно о том, что контейнеры из стандартной библиотеки не являются поточно-ориентированными. Раньше я думал, что контейнер, скажем, типа std::list, не может быть доступен более чем одному потоку одновременно (некоторые из которых могут изменять контейнер). Но теперь кажется, что это нечто большее, чем кажется на первый взгляд; что-то более тонкое, что-то не столь очевидное, по крайней мере для меня.

Например, рассмотрим эту функцию, которая принимает первый аргумент по значению :

void log(std::string msg, severity s, /*...*/) 
{
   return; //no code!
}

Является ли этот потокобезопасным?

Поначалу кажется, что она поточно-ориентированная, так как тело функции не имеет доступа к общим модифицируемым ресурсам, следовательно, поточно-ориентировано. После второго размышления мне приходит в голову, что при вызове такой функции будет создан объект типа std::string, который является первым аргументом, и я думаю, что построение этого объекта не является потокобезопасным, так как оно внутренне использует std::allocator, который, я считаю, не является потокобезопасным. Следовательно, вызов такой функции не является потокобезопасным. Но если это правильно, то что по этому поводу:

void f()
{
   std::string msg = "message"; //is it thread-safe? it doesn't seem so!
}

Я иду правильно? Можем ли мы использовать std::string (или любой контейнер, который использует std::allocator внутри) в многопоточной программе?

Я специально говорю о контейнерах как локальных переменных, в отличие от общих объектов.

Я искал в Google и нашел много похожих сомнений, без конкретного ответа. Я сталкиваюсь с той же проблемой, что и его:

Пожалуйста, рассмотрите C ++ 03 и C ++ 11, оба.

Ответы [ 4 ]

7 голосов
/ 01 марта 2012

В C ++ 11, std::allocator является потокобезопасным. Из его определения:

20.6.9.1 / 6: Примечание: память получается по телефону ::operator new(std::size_t)

и из определения ::operator new:

18.6.1.4: версии библиотек operator new и operator delete, пользовательские замены глобальных operator new и operator delete и функции стандартной библиотеки C calloc, malloc, realloc, и free должен не вводить гонки данных (1.10) в результате одновременных вызовов из разных потоков.

C ++ 03 не имел понятия о потоках, поэтому любая безопасность потоков зависела от реализации; вам нужно обратиться к документации вашей реализации, чтобы увидеть, какие гарантии она предлагает, если таковые имеются. Поскольку вы используете реализацию Microsoft, на этой странице говорится, что запись в несколько контейнерных объектов одного класса из многих потоков безопасна, что означает, что std::allocator является поточно-ориентированным.

5 голосов
/ 01 марта 2012

В C ++ 11 это было бы адресовано для распределителя по умолчанию в:

20.6.9.1 членов распределителя [allocator.members]

За исключением деструктора, функции-членыРаспределитель по умолчанию не должен вводить гонки данных (1.10) в результате одновременных вызовов этих функций-членов из разных потоков.Вызовы этих функций, которые выделяют или освобождают определенную единицу хранения, должны происходить в едином общем порядке, и каждый такой вызов освобождения должен происходить до следующего выделения (если оно есть) в этом порядке.

ЛюбойПредоставленный пользователем распределитель должен был бы придерживаться одних и тех же ограничений, если бы он собирался использоваться в разных потоках.

Конечно, для более ранних версий стандарта об этом ничего не сказано, так как они не говорилио многопоточности.Если бы реализация поддерживала многопоточность (как это делают многие или большинство), она была бы ответственна за решение этих проблем.Подобно тому, как реализации предоставляют поточно-ориентированный malloc() (и другие библиотечные функции) для C и C ++, хотя стандарты, принятые до недавнего времени, ничего не говорили об этом.

3 голосов
/ 01 марта 2012

Как вы, возможно, уже поняли, простого ответа «да» или «нет» не будет. Тем не менее, я думаю, что это может помочь:

http://www.cs.huji.ac.il/~etsman/Docs/gcc-3.4-base/libstdc++/html/faq/index.html#5_6

Я цитирую дословно:

5.6. Является ли libstdc ++ - v3 поточно-ориентированным?

libstdc ++ - v3 стремится быть поточно-ориентированным, когда все следующие условия соблюдены:

Системный libc сам по себе потокобезопасен, gcc -v сообщает модель потока, отличную от 'single', [только до версии 3.3] для рассматриваемой архитектуры существует неуниверсальная реализация atomicity.h.

2 голосов
/ 01 марта 2012

Когда std::string копируется во время вызова на log, распределитель может быть поточно-ориентированным (обязательно в C ++ 11), но сама копия не является«т.Поэтому, если есть другой поток, изменяющий исходную строку во время копирования, это не является потокобезопасным.

Вы можете получить половину строки, как это было до мутации, и еще одну половину после, или дажедоступ к освобожденной памяти в конечном итоге, если мутирующий поток перераспределил (например, добавив новые символы) или удалил строку, в то время как копирование все еще происходило.1013 *

... является потокобезопасным, если ваш распределитель является потокобезопасным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...