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

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

Некоторые концепции:

  • Если вы хотите, чтобы поток остановился, ничего не делая, AFAIK, лучший способ добиться этого — использовать условные переменные. Таким образом, когда поток не работает (или ожидает условной переменной), он будет отключен планировщиком операционной системы и не будет активен до тех пор, пока условная переменная не будет уведомлена. Такой подход предотвращает включение этого потока, когда он не работает, обеспечивая лучшее управление ресурсами, чем просто опрос переменной в цикле. В цикле опроса поток будет работать постоянно. Вы также можете поместить спящий режим в цикл для переключения потока планировщиком, но это может привести к задержкам, поскольку он должен проверять переменную после продолжительности спящего режима.
  • Представьте, что у вас есть два класса, A и B, работающие в потоках A и B соответственно. Если вы вызываете функцию класса A, работающую в потоке A, из потока B, эта функция будет выполняться в потоке B, в потоке вызывающей стороны. Чтобы запустить этот функциональный поток A, поток класса A, вы можете сказать, что он должен запустить функцию, и вы можете сказать это с помощью условной переменной.

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

Это код для него:

Эта реализация основана на идее, что функции pop и push будут вызываться из разных потоков. Когда поток A вызывает функцию pop(), он будет блокироваться до тех пор, пока элемент не будет отправлен потоком B с помощью функции push(item).

У этой реализации есть одна большая проблема. У вас нет способа остановить это. Например, в потоке A вызывается функция pop(), а исполняемый файл останавливается комбинацией ctrl-c. Поскольку поток A заблокирован условной переменной, он будет зависать в ожидании условной переменной. Единственный способ разблокировать — это вытолкнуть, а другие потоки остановлены, поэтому выскочить невозможно.

Чтобы предотвратить этот тупик, мы можем ввести логическое значение для его рабочего состояния с функцией остановки. Ниже представлена ​​обновленная версия:

В этой версии мы изменили нашу поп-функцию. Мы представили новую функцию waitPop(), которая возвращает логическое значение, и изменили нашу функцию pop(), чтобы она возвращала первый элемент в очереди, не дожидаясь условной переменной. Чтобы правильно использовать эту очередь, сначала вам нужно использовать функцию waitPop(), чтобы дождаться вставки в очередь, а когда она вернется, она сообщит вам, можете ли вы использовать pop или нет. Если он возвращает true, вы можете использовать функцию pop() для получения первого элемента, если false, то вам не следует использовать функцию pop() и выполнять обработку ошибок. Кроме того, условная переменная ищет как условие _queue.empty(), так и логическую переменную _stopped. Поэтому, когда вы вызываете функцию stop(), она уведомляет условную переменную, и если кто-либо заблокирован с помощью функции waitPop(), он будет уведомлен, а возвращаемое значение будет ложным. Так он знает, что ожидание остановлено не из-за готового предмета.

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