Как в C++ уничтожить поток, запущенный с помощью функции-члена класса и правильно ожидающий epoll?

Как говорится в вопросе, у меня есть класс. Код Pesudo выглядит следующим образом:

class MyClass
{
  MyClass() 
  { 
    epoll_fd = epoll_create(0);

    /*start a thread with MyClass::threadFunc() here */ 
    mythread = std::thread( &MyClass::threadFunc, this );
  }

  void threadFunc()
  {
    while( true )
    {
      int numEvents = epoll_wait( epoll_fd, ... );
      // some processing here
    }
  }

  ~MyClass()
  {
    mythread.join();
  }

  std::thread mythread;
  int epoll_fd;
}

int main()
{
  MyClass inst;

  // some processing

  return 0;
}

Я хотел бы, чтобы поток начинался в конструкторе и заканчивался деструктором. Однако поток имеет цикл while и ожидает событий. Следовательно, метод join() никогда не вернется, не так ли? Интересно, как лучше всего завершить выполнение потока в этом случае?

Если я использую флаг, позволяющий завершить цикл while, он будет работать только при возврате epoll_wait. Если события нет, программа застревает в методе join(), верно? Итак, в этом случае мне нужно добавить пробуждающий fd в список мониторов epoll и вручную отправить событие самому себе перед вызовом join()?

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

Raildex 21.04.2024 16:29

Вместо псевдокода извлеките минимальный воспроизводимый пример, который гораздо лучше продемонстрирует, в чем проблема. Дело в том, что я полагаю, что ваш вопрос заключается в том, как разблокировать заблокированную ветку epoll_wait(), верно? Это не имеет ничего общего с классами или функциями-членами.

Ulrich Eckhardt 21.04.2024 16:36

MyClass:threadFunc должен выполнить какую-то совместную отмену.

Pepijn Kramer 21.04.2024 16:51

Также ваш epoll_wait НЕ должен быть блочной функцией. Вам может понадобиться второй поток для заполнения очереди полученными данными (производитель), и тогда ваш threadFunc станет потребителем.

Pepijn Kramer 21.04.2024 16:58
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
89
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Интересно, как лучше всего завершить выполнение потока в этом случае?

Скажите потоку выйти, а затем присоединитесь к нему. Механизм зависит от вас.

Вы могли бы:

  • добавьте дескриптор файла в список интересов, который вы можете использовать для запуска опроса (например, канал, eventfd или что-то еще)
  • прерывание epoll_wait сигналом, после добавления соответствующих масок сигналов и логики обработки EINTR
  • переключитесь на использование epoll_pwait и отправьте этот сигнал
  • добавьте тайм-аут в свой опрос и проверьте (соответственно синхронизированную) логическую переменную, чтобы узнать, должен ли поток продолжать работать
  • и т. д.

Список должен включать в себя: создать канал, добавить конец чтения в набор epoll и записать байт в конец записи из сигнального потока... поскольку это, возможно, один из наиболее распространенных способов пробуждения цикла epoll.

Jeff Garrett 21.04.2024 18:04

eventfd лучше, чем канал, если все, что вам нужно, это разбудить epoll_wait

Jonathon Reinhart 21.04.2024 19:05

прерывание epoll_wait с сигналом после добавления соответствующих масок сигналов и логики для обработки EINTR Это может быть сложно сделать в любой сложной настройке, такой как многопоточный процесс или в библиотечном коде, где могут быть ограничения на изменение любой обработки сигнала. Вам также, вероятно, потребуется добавить какое-то другое состояние «время выхода», чтобы защититься от ложных пробуждений.

Andrew Henle 21.04.2024 19:37

Поскольку в вашей теме используется epoll(), полезно признать, что вы занимаетесь программированием либо модели актера, либо коммуникационных последовательных процессов, и вы используете epoll() в качестве reactor.

Причина, по которой это полезно, заключается в том, что это своего рода указывает на то, как должен работать этот поток (и все остальное, что с ним взаимодействует). По сути, очень хорошая идея придерживаться одной идеи: использовать epoll(), а также включать файловый дескриптор для канала IPC, по которому можно отправлять команды управления потоками. Если единственная команда, которая вам нужна, это «выйти», то получение чего угодно может означать это. Как говорится в ответе от Useless (без сомнения, совершенно неточный дескриптор для участника?), поток завершается, а затем инициатор использует join(), чтобы дождаться, пока поток фактически завершится.

Хорошая идея — придерживаться одной идеи, потому что когда вы начинаете использовать смешанные парадигмы (например, epoll() как реактор и signals), все может сильно запутаться.

Например (крайний пример): если вы используете epoll(), чтобы определить, когда сокет готов к чтению, а затем используете асинхронный ввод-вывод для фактического выполнения чтения, все может запутаться, особенно если вы начнете выдавать сигналы. слишком.

+1, если только за подход «будь проще». Многопоточную обработку достаточно сложно выполнить правильно даже без посторонних усложнений.

Andrew Henle 21.04.2024 19:40

@AndrewHenle Мне очень хорошо удалось сохранить это просто! Иногда я проходил довольно долгий путь. Имея любовь к модели актеров или CSP, мне очень нравится ZeroMQ. Однако я также признаю, что в C++ есть место для таких вещей, как std::queue<shared_ptr<>> и т. д. Итак, я использовал ZeroMQ для арбитража между потоками, используя сообщения для информирования, который теперь имеет право блокировать очередь и читать от него. Звучит сложно, но использование таких конструкций, как std::queue<<shared_ptr<>> рядом с сокетами ZMQ, обеспечивает динамику выполнения последнего со скоростью первого и безопасность памяти!

bazza 21.04.2024 22:36

Другие вопросы по теме