Мой вопрос заключается в том, предоставляют ли контейнеры, такие как вектор, набор, очередь, карта, мультикарта и т. д., свои стандартные гарантии безопасности потоков (т. е. что параллельные потоки могут получать доступ к константным членам и т. д.) независимо от того, как поток обращается к содержащемуся объекту.
Нет, не "независимо". Учитывая «одновременные потоки [доступ] const
членов» [контейнера], они могут получить постоянный доступ к сохраненным элементам, но контейнер не позволяет делать с объектами что-либо, что было бы недопустимым, если бы объекты были например локальные переменные - т. е. вы не можете вызывать методы, которые влияют на изменяемые или статические переменные, небезопасным для потоков способом.
Проще говоря: если вы заблокируете, скажем, карту для чтения, можете ли вы безопасно (насколько это касается карты) изменять содержащиеся объекты (в данном случае значение), если вы не вставляете или не удаляете элементы или иным образом вызывать неконстантные методы на карте?
Если под «заблокировать карту для чтения» вы подразумеваете, что ваша программа имеет отдельную блокировку чтения/записи и получает состояние блокировки «читатель» перед доступом к карте, тогда нет — вы не можете изменять содержащиеся объекты, если другие читатели могут получить доступ их. Чтобы сделать это безопасным, вам нужен мьютекс вокруг использования карты, как если бы потоки работали с локальной переменной.
Примеры
Ниже пустая строка разделяет примеры, а в первом и втором столбцах перечислены команды из двух потоков, которые могут выполняться в любом порядке или одновременно. Обратите внимание, что только потому, что что-то «безопасно» для выполнения, не означает, что обновление будет видно в других потоках, пока не будет выполнен какой-либо явный барьер памяти или операция очистки кеша - это зависит от вашего оборудования: «правильный» мьютекс / rwlocks и т. д. склонны заботиться об этом.
class X { int n_; std::string s_; } x;
std::vector<X> v = ...;
std::map<int,X> m = ...;
thread 1 thread 2 safe?
v.push_back(...); ++v[0].n_; Precondition: 1 <= size() < capacity()
(i.e. safe iff v[0] can't be moved)
v.some-const-member(); v.another-const-member(); YES - e.g. [n], find(), begin()
v[0].s_ = "hi"; std::cout << v[0].s_; NO - as for any string var
v[0].s_.size(); std::cout << v[0].s_; YES - as for any string var
rw_lock.r_lock() LOCKED
iterator i = m.find(7); rw_lock.w_lock() BLOCK
rw_lock.r_unlock() ....
LOCKED
std::cout << i->second; m.insert(...); YES - insert can't invalidate i
i->second.n_ += 3; m.find(7).n_ -= 3; NO - as per any int var
person
Tony Delroy
schedule
17.01.2014