Многопоточность — передача переменных между методами разных классов

#c #multithreading #boost

#c #многопоточность #повышение

Вопрос:

Я работаю над проектом, который требует многопоточности. У меня есть три потока, два из которых выполняются параллельно, а один асинхронно, как показано в примере кода. У меня есть несколько вопросов относительно переменных и boost::shared_mutex

  1. Существует ли более элегантный подход для передачи векторов и других переменных между методами?
  2. У нас возникли проблемы с 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();
}
  

**********РЕДАКТИРОВАТЬ*************

Чего я пытаюсь добиться, так это:

  1. A и B должны выполняться параллельно, и когда в A выполняются определенные критерии, a_data и b_data должны быть переданы в C. После передачи данных векторы должны продолжать собирать новые данные.
  2. 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:

Существует ли более элегантный подход для передачи векторов и других переменных между методами?

Хорошо… элегантность — дело вкуса…. Но вы можете захотеть инкапсулировать общие данные в некоторый класс или структуру, содержащую:

  1. Общие данные
  2. мьютекс (или мьютексы) для их защиты
  3. Методы, работающие с общими данными и соответствующим образом их блокирующие.

Это упростило бы объем данных, которые вам нужно передать для выполнения потока.

Вероятно, вы уже знаете это, но важно понимать, что поток — это не элемент кода, а просто концепция планирования. Тот факт, что в современных библиотеках потоки представлены объектом, это только для нашего удобства.

У нас возникли проблемы с boost::shared_mutex при блокировке критических разделов. Есть ли лучший метод в этом случае?

Без дополнительной информации о реальных проблемах трудно сказать.

Некоторые примечания:

  1. shared_mutex — это мьютекс для чтения / записи. Предназначен для одновременного чтения нескольких файлов, но только одного пользователя. Из кода кажется, что вы пишете только (unique_lock), поэтому, возможно, вы могли бы использовать более простой мьютекс.
  2. На мой взгляд, мьютексы вводятся для защиты фрагментов данных. И вы должны позаботиться о блокировке минимального количества времени и только при реальном доступе к общим данным, уравновешивая необходимость делать набор операций атомарным. У вас есть один мьютекс, который защищает два вектора. Это нормально, но вы можете подумать, нужно ли это.

Комментарии:

1. Спасибо за ваш ответ. Я могу понять, как сделать общие данные и мьютекс частью класса, но как я могу перенести методы, работающие с общими данными, в этот класс, как в моем примере, методы capture_a, capture_b и compute_c? Я немного смущен.

2. @shunyo я не очень подробно проанализировал, что вы делаете в этих методах. Это был бы вопрос о том, как вы моделируете. По крайней мере, класс, содержащий общие данные, должен иметь методы для чтения и записи в общие данные. Другие вещи могут иметь смысл, если они согласуются с тем, что представляют общие данные.