Могу ли я создать потокобезопасный std:: atomic <vector <int>>?
У меня есть функция, которая должна выполняться n=1000
раз. Эта функция выполняет моделирование методом Монте-Карло и возвращает результат int
. Я хотел бы запустить nthreads=4
параллельно. Всякий раз, когда нить заканчивает один цикл, он должен поместить результат в std::vector<int>
.
Таким образом, после 1000 циклов у меня есть вектор 1000 int
, который может быть проверен статистикой.
Так как a std::vector
не является потокобезопасным, я думал о std::mutex
(который, несомненно, сработает).
Но мне интересно, могу ли я объявить вектор атомарным и, таким образом, обойти мьютексы?
Возможно ли иметь std::atomic<std::vector<int>>
? И могу ли я использовать push_back
и т.д. На нем?
Ответы
Ответ 1
Вам не нужно. Все в порядке, чтобы получить доступ к ja std::vector
из нескольких потоков, если
- вы читаете объекты
- вы пишете на разные объекты
Поэтому просто убедитесь, что вы создаете вектор размера n=1000
и в зависимости от вашего номера потока (от 1 до 4) вы назначаете в свои потоки элементы 0-249, 250-499 и т.д.
Итак, каждый из ваших потоков вычисляет элементы n/nthreads
.
Ответ 2
С++ 11 §29.5/1 говорит
Существует общий шаблонный шаблон. Тип аргумента шаблона T должен быть тривиально скопируемым (3.9).
Что означает тривиальное копирование?
В §3.9 говорится
Скалярные типы, тривиально-скопируемые типы классов (раздел 9), массивы таких типов и cv-квалификационные версии этих типов (3.9.3) совместно называются тривиально-скопируемыми типами.
Для типов классов (из которых std::vector
is):
Тривиально-скопируемый класс - это класс, который:
- не имеет нетривиальных конструкторов копирования
- не имеет нетривиальных конструкторов перемещения
- не имеет нетривиальных операторов присваивания копий
- не имеет нетривиальных операторов присваивания перемещения
- имеет тривиальный деструктор
В соответствии с этим списком std::vector
не является тривиально скопируемым, поэтому вы не можете использовать std::atomic<std::vector<int>>
.
Поскольку вы знаете размер заранее, и поскольку вам не нужно использовать методы, которые требуют, чтобы вектор перераспределялся в другом месте (например, push_back)
. Вы можете использовать std::vector<int>::resize
или конструктор размера для предварительного распределения и предконструкции требуемый int
s. Поэтому ваши параллельные потоки не должны работать с самим вектором, а с элементами.
Если нет доступа из разных потоков к одному и тому же элементу, условие расы отсутствует.
То же самое относится к int k[1000]
, который тривиально можно копировать. Но вам это не нужно, поскольку потоки не меняют сам массив/вектор/список, а элементы.
Ответ 3
Атомный может быть создан с помощью тривиально копируемых типов. Вектор не такой тип.