Вчера я опубликовал что-то в похожем направлении, но этот вопрос был конкретно о мьютексах, и я не нашел большого ответа в упомянутой «дублирующей» ветке. Я хочу попытаться задать более общий вопрос сейчас, я надеюсь, что все в порядке.
Посмотрите на этот код:
#include <iostream>
#include <mutex>
#include <vector>
#include <initializer_list>
using namespace std;
class Data {
public:
void write_data(vector<float>& data) {
datav = move(data);
}
vector<float>* read_data() {
return(&datav);
}
Data(vector<float> in) : datav{ in } {};
private:
vector<float> datav{};
};
void f1(vector<Data>& in) {
for (Data& tupel : in) {
vector<float>& in{ *(tupel.read_data()) };
for (float& f : in) {
f += (float)1.0;
};
};
}
void f2(vector<Data>& in) {
for (Data& tupel : in) {
vector<float>& in{ *(tupel.read_data()) };
for (float& f : in) {
cout << f << ",";
};
};
}
int main() {
vector<Data> datastore{};
datastore.emplace_back(initializer_list<float>{ 0.2, 0.4 });
datastore.emplace_back(initializer_list<float>{ 0.6, 0.8 });
vector<float> bigfv(50, 0.3);
Data demo{ bigfv };
datastore.push_back(demo);
thread t1(f1, ref(datastore));
thread t2(f2, ref(datastore));
t1.join();
t2.join();
};
В ожидании я бы предположил, что получу дикую смесь выходных значений в зависимости от того, какой поток первым получил значение вектора, поэтому в третьем векторе «демо» с 50x0,3f я бы ожидал смесь 0,3 (t2 добрался там первым) и 1,3 (t1 получил его первым) как вывод. Несмотря на то, что я пытался использовать как можно больше прямых ссылок, прямых указателей и т. Д. 1017 *, чтобы избежать копирования (исходный проект использует довольно большие объемы данных), код ведет себя определенным образом (всегда t2, затем t1-доступ). Почему? Разве я не обращаюсь к плавающим объектам напрямую по ссылке в обеих функциях потока?
Как бы вы сделали этот векторный доступ четким? Единственными возможными решениями, которые я нашел в другом потоке, были:
-определение массива unique_ptr схожего размера с мьютексами (чувствует себя плохо, потому что мне нужно иметь возможность добавлять контейнеры данных в хранилище данных, так что это будет означать очистку массив и перестроение его каждый раз, когда я изменяю размер хранилища данных?), или
- делаем доступ к вектору atomi c (который, как мысль, делает мою работу так, как я хочу, чтобы она была потокобезопасной, но нет атома c, инвариантного для вектора, или есть в какой-то не-STL-lib?), или
-записать обертку для мьютекса в классе данных?
Это для моего проекта не важно, какой поток обращается первым, важно только то, что я могу однозначно прочитать / записать весь вектор в набор данных потоком, при этом другой поток не манипулирует набором данных одновременно.