Я пытаюсь контролировать многопоточный доступ к вектору данных , который имеет фиксированный размер , поэтому потоки будут ждать, пока их текущая позиция в нем не будет заполнена, прежде чем пытаться его использовать, или заполнят это сами, если еще никто. (Но убедитесь, что никто не ждет, если их позиция уже заполнена, или никто еще не сделал этого)
Однако я изо всех сил пытаюсь найти хороший способ сделать это, особенно с использованием std :: Atomi c. Я просто не очень знаком с концепциями многопоточности C ++, кроме использования базовых c std::thread
.
Вот очень грубый пример проблемы:
class myClass
{
struct Data
{
int res1;
};
std::vector<Data*> myData;
int foo(unsigned long position)
{
if (!myData[position])
{
bar(myData[position]);
}
// Do something with the data
return 5 * myData[position]->res1;
}
void bar(Data* &data)
{
data = new Data;
// Do a whole bunch of calculations and so-on here
data->res1 = 42;
}
};
Теперь представьте, если foo()
называется многопоточным, а несколько потоков может (или не может) иметь один и тот же position
одновременно. Если это произойдет, существует вероятность того, что поток может (между созданием Data
и завершением bar()
попытаться фактически использовать данные.
Итак, каковы варианты ?
1: Создайте std :: mutex для каждой позиции в myData. Что, если в myData есть 10 000 элементов? Это 10 000 std :: mutexes, не очень. : Поместите вокруг него lock_guard
следующим образом:
std::mutex myMutex;
{
const std::lock_guard<std::mutex> lock(myMutex);
if (!myData[position])
{
bar(myData[position]);
}
}
Хотя это работает, это также означает, что, если разные потоки работают в разных позициях, они без необходимости ждут, тратя все преимущество потоков.
3: использовать вектор символов и спин-блокировку как мьютекс бедного человека? Вот как это может выглядеть:
static std::vector<char> positionInProgress;
static std::vector<char> positionComplete;
class myClass
{
struct Data
{
int res1;
};
std::vector<Data*> myData;
int foo(unsigned long position)
{
if (positionInProgress[position])
{
while (positionInProgress[position])
{
; // do nothing, just wait until it is done
}
}
else
{
if (!positionComplete[position])
{
// Fill the data and prevent anyone from using it until it is complete
positionInProgress[position] = true;
bar(myData[position]);
positionInProgress[position] = false;
positionComplete[position] = true;
}
}
// Do something with the data
return 5 * myData[position]->res1;
}
void bar(Data* data)
{
data = new Data;
// Do a whole bunch of calculations and so-on here
data->res1 = 42;
}
};
Этот , кажется, работает, но ни одна из операций тестирования или установки не является атомом c, поэтому у меня такое ощущение, что мне просто везет.
4: А как насчет std::atomic
и std::atomic_flag
? Ну, есть несколько проблем .
std::atomic_flag
не имеет пути к test
без установки в C ++ 11 ... что Это очень сложно. std::atomic
не является подвижным или копируемым, поэтому я не могу сделать из них вектор (я не знаю количество позиций во время построения myClass)
Заключение:
Это самый простой пример, который (вероятно) компилируется, который я могу придумать, который демонстрирует мою реальную проблему. На самом деле myData - это двумерный вектор, реализованный с использованием специального решения, созданного вручную, сами данные - это вектор указателей на более сложные типы данных, данные просто не возвращаются, и т. Д. c. Это лучшее, что я мог придумать.