Как лучше всего заблокировать все потоки, кроме одного?

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

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

Изменить: я использую Keil RTX 5/CMSIS RTOS 2 на чипе STM32H753.

Спасибо


person wolly981    schedule 06.01.2020    source источник
comment
Я бы поставил под сомнение ваши приоритетные назначения и декомпозицию задач, если вы считаете, что критическая секция или блокировка расписания необходимы — это часто указывает на недостаток дизайна. Мьютексы можно использовать для синхронизации потоков, но вам, похоже, нужна блокировка асинхронного планировщика.   -  person Clifford    schedule 13.01.2020
comment
Во-первых, давайте устраним недоразумение: мьютексы обычно связаны с каким-то конкретным ресурсом (например, периферийным устройством I²C для шины с несколькими подчиненными микросхемами или некоторой глобальной переменной). В случае, который вы упомянули, защищаемым ресурсом является ЦП и согласованность контекста выполнения в течение периода, когда вы хотите заблокировать другие задачи. То есть вы можете использовать мьютекс в принципе для достижения того, что вы описываете. С другой стороны, вы правы в том, что каждая задача, которая должна быть заблокирована от критической секции, должна пытаться получить этот мьютекс, поэтому все соответствующие функции теперь нуждаются в исправлениях.   -  person HelpingHand    schedule 01.05.2020


Ответы (3)


ОСРВ CMSIS имеет пару функций osKernelLock() и osKernelUnlock() — динамическое изменение приоритетов потоков или использование мьютексов не нужно и, вероятно, опрометчиво.

Любая другая RTOS будет иметь аналогичный API критической секции.

Обратите внимание, что это только предотвращает переключение контекста задачи; это не предотвращает выполнение прерываний. Обычно это желательно, но если вы хотите предотвратить это, вы можете просто отключить все прерывания, используя _disable_irq()/_enable_irq(). Это предотвратит переключение задач и прерывания.

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

person Clifford    schedule 13.01.2020

Какую ОСРВ вы используете? Я предполагаю, что вы используете RTOS на основе приоритетов.

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

Если вы хотите, чтобы один поток выполнялся исключительно, сделайте этот поток потоком с наивысшим приоритетом. Планировщик RTOS запустит поток с наивысшим приоритетом, который готов к запуску. Если ваш поток имеет наивысший приоритет и не блокирует себя, другие потоки не будут выполняться. В CMSIS-RTOS вы можете изменить приоритет потока с помощью osThreadSetPriority().

person kkrambo    schedule 06.01.2020
comment
Повышение приоритета одного потока не блокирует все другие потоки с более низким приоритетом, если только не доступно только одно ядро ​​ЦП. - person Maxim Egorushkin; 06.01.2020
comment
Если используется CMSIS-RTOS (что, вероятно, формирует теги), то osKernelLock()/osKernelUnlock() будет более подходящим, чем динамическое изменение приоритетов потоков. Даже если нет, я сомневаюсь, что есть какая-либо RTOS, достойная этого имени, которая не поддерживает подобную критическую секцию. - person Clifford; 13.01.2020
comment
Исправление проблем критической секции путем назначения разных приоритетов часто приводит к следующей проблеме. Приоритеты должны быть назначены в соответствии с требованиями времени отклика задач или другими, более важными критериями. Многие критические участки встречаются в менее типичных моментах (например, при включении питания), когда задача, которая должна иметь низкий приоритет, должна выполняться без прерывания в течение короткого периода времени. - person HelpingHand; 01.05.2020

Не меняйте приоритеты задач прямо из пользовательского кода. Большинство RTOS предоставляют API, которые позволяют нам это делать, но это плохой стиль, поскольку он порождает больше проблем, чем решает. Исключение составляют случаи, когда некоторые RTOS работают с этим внутри (например, мьютексы с приоритетным наследованием, чтобы избежать определенных проблем с многозадачностью).

Я предполагаю, что вы хотите иметь более длинную критическую секцию только на этапе включения вашей системы или на другом особом этапе ее работы. В противном случае вам действительно следует прислушаться к комментарию @Clifford и задать вопросы о ваших приоритетных назначениях и декомпозиции задач.

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

Управление уровнями выполнения

Самый простой способ реализовать это — написать небольшую библиотеку поверх вашей RTOS, используя два ресурса счетных семафоров: один для текущего уровня выполнения, другой — для следующего. При входе на уровень выполнения семафор заполняется столько токенов, сколько есть задач, которые должны находиться под контролем управления уровнем выполнения. Каждая задача, ожидающая обработки данного уровня выполнения, пытается получить свой маркер с семафора current-runlevel. Когда задача завершает свою часть уровня выполнения, она обращается к семафору следующего уровня, который в это время будет недоступен.

Прежде чем заполнять конфигурацию управления уровнем запуска, вы можете нарисовать себе диаграмму последовательности, чтобы проверить, в каких точках задачи должны ждать других по любой причине. Обычно для каждой задачи релевантны только несколько уровней выполнения, и для каждого уровня выполнения набор соответствующих задач также может быть небольшим. Поэтому вы можете добавить небольшую вспомогательную функцию, такую ​​как WaitForRunlevelNumber(N), с циклом, который автоматически работает с теми уровнями выполнения, которые не имеют отношения к делу.

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

person HelpingHand    schedule 01.05.2020