#c #multithreading #boost
#c #многопоточность #повышение
Вопрос:
Я работаю над проектом, который требует многопоточности. У меня есть три потока, два из которых выполняются параллельно, а один асинхронно, как показано в примере кода. У меня есть несколько вопросов относительно переменных и boost::shared_mutex
- Существует ли более элегантный подход для передачи векторов и других переменных между методами?
- У нас возникли проблемы с
boost::shared_mutex
блокировкой критических разделов. Есть ли лучший метод в этом случае?
Спасибо за вашу помощь. Извините за длину кода.
// A.h
class A{
private:
std::vector<int> a_data;
public:
int capture_a(std::vector<int> amp;data_transfer,boost::shared_mutex amp;_access,int amp;flag);
};
// A.cpp
A::capture_a(std::vector<int> amp;a_data_transfer,boost::shared_mutex amp;_access,int amp;a_flag)
{
// collect data
while(true)
{
//data input
a_data.push_back(data);
if(a_data.size() > N) //a certain limit
{
// save a_data
a_flag = 1;
boost::unique_lock< boost::shared_mutex > lock(_access);
//swap with empty array
// a_data_transfer should be empty
a_data_transfer.swap(a_data);
}
if(boost::thread_interruptedamp;)
{
std::cout << " Thread interrupted" << std::endl;
return 0;
}
}
}
// B.h
class B{
private:
std::vector<int> b_data;
public:
int capture_b(std::vector<int> amp;b_data_transfer, boost::shared_mutex amp;_access,int amp;a_flag,int amp;b_flag);
};
// B.cpp
B::capture_b(std::vector<int> amp;b_data_transfer, boost::shared_mutex amp;_access, int amp;a_flag,int amp;b_flag)
{
// collect data
while(true)
{
//data input
b_data.push_back(data);
if(a_flag == 1) //a_flag is true
{
boost::unique_lock< boost::shared_mutex > lock(_access);
b_data_transfer.swap(b_data);
b_flag = 1;
// save data
a_flag = 0;
}
if(boost::thread_interruptedamp;)
{
std::cout << " Thread interrupted" << std::endl;
return 0;
}
}
}
// C.h
class C
{
private:
std::vector<int> c_data;
public:
int compute_c(std::vector<int> amp;a_data,std::vector<int> amp;b_data,boost::shared_mutex amp;_access, int amp;b_flag);
}
// C.cpp
C::compute_c(std::vector<int> amp;a_data,std::vector<int> amp;b_data,boost::shared_mutex amp;_access,int amp;b_flag)
{
while(true) {
if(b_flag == 1)
{
boost::unique_lock< boost::shared_mutex > lock(_access);
// compute on c
c_data = a_data b_data; // for example
// save c_data
b_flag = 0;
a_data.clear();
b_data.clear();
}
if(boost::thread_interruptedamp;)
{
std::cout << " Thread interrupted" << std::endl;
return 0;
}
}
}
int main()
{
std::vector<int> a_data_transfer, b_data_transfer;
boost::shared_mutex _access;
int a_flag = 0, b_flag = 0;
boost::thread t1(amp;A::capture_a,boost::ref(a_data_transfer),boost::ref(_access),boost::ref(a_flag));
boost::thread t2(amp;B::capture_b,boost::ref(b_data_transfer),boost::ref(_access),boost::ref(a_flag),boost::ref(b_flag));
boost::thread t3(amp;C::compute_c,boost::ref(a_data_transfer),boost::ref(b_data_transfer),boost::ref(_access),boost::ref(b_flag));
// Wait for Enter
char ch;
cin.get(ch);
// Ask thread to stop
t1.interrupt();
t2.interrupt();
t3.interrupt();
// Join - wait when thread actually exits
t1.join();
t2.join();
t3.join();
}
**********РЕДАКТИРОВАТЬ*************
Чего я пытаюсь добиться, так это:
- A и B должны выполняться параллельно, и когда в A выполняются определенные критерии,
a_data
иb_data
должны быть переданы в C. После передачи данных векторы должны продолжать собирать новые данные.- C должен принимать
a_data
иb_data
и выполнять вычисления, когда флаг имеет значение true .
Проблема с boost::shared_mutex — мы хотим a_data_transfer
, чтобы при замене было пусто. Это случается иногда, но не всегда. Мне нужен способ убедиться, что это происходит для правильной работы кода.
Комментарии:
1. Какие у вас проблемы? Функция capture_a не выглядит хорошо спроектированной.
2. на и происходит гонка данных
a_flag
b_flag
. Кроме того, «Одна большая блокировка» предотвратит масштабирование. Наконец, похоже, что там подойдет обычный мьютекс. Пожалуйста, просто опишите, чего вы пытаетесь достичь, без (ошибочных) идей о том, как это решить. Я думаю, что есть несколько простых шаблонов, которые подошли бы, но прямо сейчас код… недостаточно, чтобы сказать мне, что вы хотели , чтобы произошло.3. Я отредактировал вопрос, чтобы описать, чего мы пытаемся достичь. Надеюсь, это поможет.
4. Задействован ли ввод-вывод? Звучит как работа для параллелизма на основе акторов (с повышением Asio?)
5. Да, ввод-вывод задействован. Как для A, так и для B мы получаем данные (с разной скоростью), и мы также должны записывать их в файлы
Ответ №1:
Существует ли более элегантный подход для передачи векторов и других переменных между методами?
Хорошо… элегантность — дело вкуса…. Но вы можете захотеть инкапсулировать общие данные в некоторый класс или структуру, содержащую:
- Общие данные
- мьютекс (или мьютексы) для их защиты
- Методы, работающие с общими данными и соответствующим образом их блокирующие.
Это упростило бы объем данных, которые вам нужно передать для выполнения потока.
Вероятно, вы уже знаете это, но важно понимать, что поток — это не элемент кода, а просто концепция планирования. Тот факт, что в современных библиотеках потоки представлены объектом, это только для нашего удобства.
У нас возникли проблемы с boost::shared_mutex при блокировке критических разделов. Есть ли лучший метод в этом случае?
Без дополнительной информации о реальных проблемах трудно сказать.
Некоторые примечания:
- shared_mutex — это мьютекс для чтения / записи. Предназначен для одновременного чтения нескольких файлов, но только одного пользователя. Из кода кажется, что вы пишете только (unique_lock), поэтому, возможно, вы могли бы использовать более простой мьютекс.
- На мой взгляд, мьютексы вводятся для защиты фрагментов данных. И вы должны позаботиться о блокировке минимального количества времени и только при реальном доступе к общим данным, уравновешивая необходимость делать набор операций атомарным. У вас есть один мьютекс, который защищает два вектора. Это нормально, но вы можете подумать, нужно ли это.
Комментарии:
1. Спасибо за ваш ответ. Я могу понять, как сделать общие данные и мьютекс частью класса, но как я могу перенести методы, работающие с общими данными, в этот класс, как в моем примере, методы capture_a, capture_b и compute_c? Я немного смущен.
2. @shunyo я не очень подробно проанализировал, что вы делаете в этих методах. Это был бы вопрос о том, как вы моделируете. По крайней мере, класс, содержащий общие данные, должен иметь методы для чтения и записи в общие данные. Другие вещи могут иметь смысл, если они согласуются с тем, что представляют общие данные.