Является std::string безопасным с gcc 4.3?
Я разрабатываю многопоточную программу, запущенную на Linux (скомпилированную с g++ 4.3), и если вы немного поискаете, вы найдете много страшных историй о std::string, которые не являются потокобезопасными с GCC. Это, предположительно, связано с тем, что внутри он использует copy-on-write, который наносит ущерб инструментам вроде Helgrind.
Я сделал небольшую программу, которая копирует одну строку в другую строку, и если вы проверите обе строки, они оба будут иметь один и тот же внутренний указатель _M_p. Когда одна строка изменена, указатель изменяется, поэтому материал "копировать-на-запись" работает нормально.
Я беспокоюсь о том, что происходит, если я разделяю строку между двумя потоками (например, передавая ее как объект в потоковом потоке данных между двумя потоками). Я уже пробовал компиляцию с параметром "-pthread", но это, похоже, не имеет большого значения. Поэтому мой вопрос:
- Есть ли способ заставить std::string быть потокобезопасным? Я бы не возражал, если для этого было отключено поведение "копирование по-записи".
- Как другие люди решили это? Или я параноик?
Я не могу найти окончательного ответа, поэтому надеюсь, что вы, ребята, можете мне помочь..
Edit:
Ничего себе, за это очень много ответов. Спасибо! Я обязательно буду использовать решение Jack, когда я хочу отключить COW. Но теперь главный вопрос: действительно ли мне нужно отключить COW? Или это "бухгалтерия", сделанная для поточной безопасности COW? В настоящее время я просматриваю источники libstdС++, но это займет довольно много времени, чтобы выяснить...
Изменить 2
OK просмотрел исходный код libstdС++, и я нашел что-то вроде этого в libstd ++ - v3/include/bits/basic_string.h:
_CharT*
_M_refcopy() throw()
{
#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING
if (__builtin_expect(this != &_S_empty_rep(), false))
#endif
__gnu_cxx::__atomic_add_dispatch(&this->_M_refcount, 1);
return _M_refdata();
} // XXX MT
Итак, есть что-то там об атомных изменениях на счетчике...
Заключение
Я отмечаю комментарий sellibitze как ответ здесь, потому что, думаю, мы пришли к выводу, что эта область до сих пор не решена. Чтобы обойти поведение COW, я бы предложил ответить Джеку Ллойду. Спасибо всем за интересное обсуждение!
Ответы
Ответ 1
Нити еще не являются частью стандарта. Но я не думаю, что любой поставщик может уйти, не делая std::string потокобезопасным, в настоящее время. Примечание. Существуют разные определения "поточно-безопасные", и мои могут отличаться от ваших. Конечно, нет смысла защищать контейнер, например std::vector, для одновременного доступа по умолчанию, даже если он вам не нужен. Это противоречило бы духу С++, "не платите за то, что вы не используете". Пользователь должен всегда отвечать за синхронизацию, если он хочет делиться объектами между различными потоками. Здесь проблема заключается в том, использует ли библиотечный компонент и разделяет некоторые скрытые структуры данных, которые могут привести к расам данных, даже если "функции применяются к различным объектам" с точки зрения пользователя.
Проект С++ 0x (N2960) содержит раздел "Уклонение от гонки данных", в котором в основном говорится, что компоненты библиотеки могут обращаться к общим данным, скрытым от пользователя, тогда и только тогда, когда он активно избегает возможных гонок данных. Похоже, что реализация std:: basic_string для копирования на запись должна быть безопасной w.r.t. многопоточность в качестве другой реализации, где внутренние данные никогда не используются для разных экземпляров строк.
Я не уверен, что libstdС++ позаботится об этом уже на 100%. Я думаю, что так и есть. Разумеется, посмотрите документацию
Ответ 2
Если вы не против отключения копирования на запись, это может быть лучший способ действий. std::string COW работает только в том случае, если он знает, что он копирует другой std::string, поэтому вы можете заставить его всегда выделять новый блок памяти и делать фактическую копию. Например, этот код:
#include <string>
#include <cstdio>
int main()
{
std::string orig = "I'm the original!";
std::string copy_cow = orig;
std::string copy_mem = orig.c_str();
std::printf("%p %p %p\n", orig.data(),
copy_cow.data(),
copy_mem.data());
}
покажет, что вторая копия (с использованием c_str) предотвращает COW. (Потому что std::string видит только голый const char * и не знает, откуда он пришел или что может быть его время жизни, поэтому он должен создать новую частную копию).
Ответ 3
Этот раздел из внутренних компонентов libstdС++:
Функциональность строки библиотеки С++ требует нескольких атомных операций для обеспечения безопасности потока. Если вы не предпринимать какие-либо специальные действия, библиотека будут использовать версии-заглушки этих функции, которые не являются потокобезопасными. Они будут работать нормально, если только ваши приложения многопоточны.
Счет ссылок должен работать в многопоточной среде. (если ваша система не предоставляет необходимые атомы)
Ответ 4
Контейнер STL не является потокобезопасным. Таким образом, библиотека имеет общую цель (как для однопоточного, так и для многопотокового режима). В многопоточности вам нужно будет добавить механизм синхронизации.
Ответ 5
Кажется, что это было исправлено некоторое время назад: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=5444 был (закрыт как та же проблема, что http://gcc.gnu.org/bugzilla/show_bug.cgi?id=5432, который был исправлен в 3.1).
См. также http://gcc.gnu.org/bugzilla/show_bug.cgi?id=6227
Ответ 6
В соответствии с эта проблема с ошибкой, реализация std::basic_string
copy-on-write все еще не полностью поточно-безопасна. <ext/vstring.h>
- это реализация без COW и, похоже, намного лучше в контексте только для чтения.