Обмен данными в реальном времени между потоками - PullRequest
0 голосов
/ 08 октября 2018

Для проекта встраиваемых систем мне приходится иметь дело с датчиками, которые отправляют много данных одновременно.В настоящее время каждый датчик имеет свой собственный поток, и многие потоки имеют ссылки друг на друга.

Используя блокировку мьютекса, эти потоки получают данные друг от друга.Однако во время производства некоторые потоки неопределенно ждут, пока другой поток завершит работу с заблокированными данными.Я знаю, что проблема связана с взаимоблокировкой, но я считаю, что эти проблемы трудно найти и предотвратить.

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

```
template<class T>
T SharedData<T>::Get() {
  LockGuard lock(mutex_);
  T data = data_;
  if (!IsValid() && has_default_value_) {
    data = default_value_;
  }

  return data;
}

template<class T>
void SharedData<T>::Set(T data) {
  is_set_ = true;
  set_time_ = system_clock::now();

  LockGuard lock(mutex_);
  data_ = data;
}
```

Мой вопрос заключается в следующем;Что такое хороший и безопасный способ обмена данными в реальном времени между потоками (желательно без использования мьютексов)?

Я ищу решение в направлении передачи сообщений между потоками.Я еще не нашел элегантный способ сделать это.

Заранее спасибо!

Редактировать: чтобы уточнить «потоки, получающие данные друг от друга», вот фрагмент кода:

void MotorMessage::SetConnectedModules(MotorSensor &motor_sensor) {
  out_buffer_[index_++] = motor_sensor.connected_.Get();
}

Здесь motor_sensor - это ссылка на другой поток, а connected_ - это тип SharedData.

Ответы [ 2 ]

0 голосов
/ 08 октября 2018

Если вы разрабатываете свою программу таким образом, что ни один поток не пытается заблокировать второй мьютекс, в то время как уже удерживает первый заблокированный мьютекс, то ваша программа гарантированно никогда не заблокируется.

Если вы можете 'Чтобы справиться с этим, вы все равно можете гарантировать, что ваша программа никогда не заблокируется, если вы убедитесь, что всякий раз, когда какой-либо поток пытается заблокировать более одного мьютекса за раз, он пытается заблокировать эти два (или более) мьютекса в том же порядке, что и все остальные.Поток пытается заблокировать их. (т. е. только когда поток # 1 делает lock(mutex_a); lock(mutex_b);, а поток # 2 делает lock(mutex_b); lock(mutex_a);, вы открываете возможность, что тупик может - и, следовательно, в конечном итоге - происходит)

Что касается того, как это сделать, если ваша программа достаточно мала, чтобы по-прежнему практично ее перепроектировать, то хорошим вариантом будет использование передачи сообщений между потоками, а не совместного использования.данные.То есть, если поток A имеет некоторые данные, о которых поток B должен знать, поток A должен обернуть эти данные в некоторый объект сообщения / события и отправить этот объект в очередь сообщений / событий, о которой поток B будет уведомлени проверить на потом.(Так как ни публикация события А, ни получение события Б никогда не будут блокироваться в течение более чем небольшого / конечного промежутка времени, при таком подходе исключается любая вероятность взаимных блокировок). Обратите внимание, что даже если объем данных, которые вы хотите передатьмежду потоками велика, этот метод все еще будет эффективен, если вы передаете данные через shared_ptr, а не делаете копию данных.

Если, OTOH, ваша программа уже слишком велика / сложна, чтобысделать реорганизацию выполнимой, другой вариант - проанализировать и отладить причину взаимоблокировки вашей программы и внести необходимые изменения, чтобы избежать этих тупиков.Такие инструменты, как helgrind * компании Valgrind *, могут помочь с этим, автоматически обнаруживая несогласованную блокировку мьютекса во время выполнения и сообщая вам об этом.Кроме того, если вы можете перевести вашу программу в заблокированное состояние, отладчик может показать вам, где заблокирован каждый поток, и это может привести вас к пониманию того, где в вашей кодовой базе существует непоследовательный порядок блокировки, который допускает возникновение взаимоблокировки.

Кроме того, вы всегда можете снабдить каждую команду блокировкой / разблокировкой printf(), которая включает в себя как идентификатор потока, так и уникальный идентификатор мьютекса (например, его местоположение в памяти), а затем просмотреть полученный результат.log-output для поиска противоречивых шаблонов блокировки в выходных данных.Этот анализ журнала может быть автоматизирован с помощью инструмента, если становится слишком трудно сделать это вручную.

0 голосов
/ 08 октября 2018

Вы можете настроить одну или несколько атомарных очередей от потоков датчика до потребителя (ей).Таким образом, вам не нужно выполнять какую-либо блокировку самостоятельно.

Например, очередь из Intel TBB .

...