proposal for standardization of concurrent maps объясняет, почему в параллельных контейнерах нет erase
. (Искать раздел «Почему параллельное стирание не поддерживается».)
Контейнеры в стандартной библиотеке C++ несут ответственность за удаление их содержимого. Содержимое «находится» в контейнере, подобно тому, как элемент находится в «массиве». operator[]
возвращает ссылку на содержащийся объект и после возврата ссылки контейнер не знает, как долго запрашивающий поток «зависает» по ссылке. Поэтому, если другой поток приходит с просьбой о стирании элемента, контейнер не имеет понятия, безопасен ли элемент для удаления.
Я пытался думать о чем-то вы могли бы работать вокруг ограничения, но объяснение проблемы стирания вызывает еще больший вопрос: это всегда абонентов ответственности за защиту одновременного доступа к элементы, хранящиеся на карте.
Например: предположим, что у вас есть одновременное отображение из int
в float
и вы это делаете:
thread A thread B
the_map[99] = 0.0;
...
the_map[99] = 1.0; if (the_map[99] == 1.0) ... // data race!!!
Причиной этого является гонка данных, что даже если выражение the_map[99]
защищена, она возвращает ссылка, доступ к которой не защищен. Так как обращения к ссылке не защищены, в память, на которую ссылаются эта точка, разрешено только чтение. (Или вам нужно заблокировать все обращения к этой памяти).
Вот мере дорогая альтернатива я могу думать (и это действительно дорого):
typedef concurrent_unordered_map<MyKey_t, atomic<MyMapped_t*> > MyContainer_t;
Теперь, глядя вверх пункт означает:
MyMapped_t* x = the_map[a_key].load();
вставив элемент :
the_map[a_key].store(ptr_to_new_value);
удаление элемента является:
the_map[a_key].store(0);
и тестирование ли элемент на самом деле в карте есть:
if (the_map[a_key].load() != 0) ...
Наконец, если вы собираетесь делать какие-либо условного стирания (или модификации) имеет чтобы быть что-то более сложным, чем: (. выше неправильно, потому что он страдает от the ABA problem)
MyMapped_t* x;
do {
x = the_map[a_key].load();
} while (condition_for_erasing(x) && !the_map[a_key].compare_exchange_strong(x, 0));
Несмотря на это: это по-прежнему не защищает вас от одновременных изменений в базовом MyMapped_t
и требует, чтобы вы выполняли всю конструкцию, управление хранением и уничтожение MyMapped_t
.
:(
(http://msdn.microsoft.com/en-us/library/vstudio/hh750089.aspx) – BoBTFish
Цитата [Для ленивых.]: «Методы стирания начинаются с unsafe_, чтобы указать, что они не являются безопасными для параллелизма ». Это имеет место как для реализации TBB, так и для ms. –
@ ZamfirKerlukson Я думаю, что искатель понимает, что эти методы небезопасны, но не понимают, как их следует использовать таким образом, чтобы сделать их безопасными. И, глядя на документацию, я не уверен сам. Я думаю, вы просто не можете этого сделать, пока какой-либо другой поток может выполнять какую-либо операцию (безопасно или иначе) с контейнером. то есть только тогда, когда ни одна другая нить не использует его. Который кажется глупым, поскольку вы должны ввести замок вокруг использования в любом случае, не так ли? Я полагаю, что эти контейнеры эффективны, если вам не нужны небезопасные операции в течение параллельной фазы вашей программы. – BoBTFish