2012-03-01 3 views
12

Я знаю, что контейнеры из стандартной библиотеки не являются потокобезопасными. При этом я думал, что контейнер, например тип 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, как.

+0

Nawaz - знаете ли вы, о какой реализации C++ они говорят здесь: http://www.sgi.com/tech/stl/Allocators.html Указывает, что распределитель по умолчанию является потокобезопасным. – Sid

+0

@Sid: Это не стандартная библиотека; они поддерживают множество вещей, которые stdlib не делает. – Nawaz

+0

Хорошо, извините за отвлечение. – Sid

ответ

7

В 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, this page говорит, что безопасно записывать в несколько объектов контейнера одного класса из многих потоков, что означает, что std::allocator является потокобезопасным.

+0

Это C++ 11. Как насчет C++ 03? C++ 03 не имел понятия многопоточности. – Nawaz

+0

Ну, я искал C++ 03, и кажется, что распределитель C++ 03 не является потокобезопасным. – Nawaz

+0

@Nawaz: Действительно, C++ 03 ничего не говорит о безопасности потоков, поэтому вам придется обратиться к документации вашего компилятора/библиотеки. –

5

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

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

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

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

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

3

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

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

системы является сам потокобезопасной, НКУ -v сообщает потоковую модель, кроме «единого», [только для версии 3.3] для этой архитектуры существует не общая реализация atomicity.h.

+0

Речь идет о конкретной реализации lib в очень старом компиляторе ... 1 –

+0

@ DavidRodríguez-dribeas Я думаю, что большинство ответов, которые мы найдем на этот вопрос, будут зависеть от того, что компилятор/версия C++, а также то, что реализация malloc быть использованным. – Sid

2

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

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


Ото, и ...

std::string msg = "message"; 

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

+0

"* Так что если есть другой поток, изменяющий исходную строку во время копирования, это не безопасно для потоков. *". Это другая проблема. Я не говорю об этом, так как в этом случае проблема заключается в чтении общего объекта, в то время как я говорю о локальных объектах. – Nawaz

+1

@Nawaz Тогда вся проблема сводится к безопасности потока вашего распределителя, потому что это единственное, что разделяется между потоками. –

 Смежные вопросы

  • Нет связанных вопросов^_^