Перейти к содержанию
    

Обмен данными между задачами RTOS

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

Имеем некоторую RTOS и 2 задачи для простоты.

Задача task1 имеет приоритет выше, чем task2. Задача task1 висит в ожидании данных от некоторой периферии.

Задача task2 должна принимать ПОСЛЕДНИЕ данные от task1, т.е если задача task1 получила скажем 10 буферов данных, то нам нужен в task2 только последний буфер, остальные можем безболезненно потерять (т.е. очередь не нужна).

Важное условие - код task2 должен выполняться как можно быстрее, поэтому я мьютекс поместил внутрь опроса флага, т.е. чтобы данные блокировались только когда они пришли, а не каждый цикл выполнения task2.

Прав ли я с таким кодом, покритикуйте, пожалуйста.

 

int a,b,c;
int buf[3];

task1
{
while(1)
{
  if (wait_peri_data(&buf))
  {
    mutex_lock();
    a = buf[0];
    b = buf[1];
    c = buf[2];
    mutex_unlock();
    peri_data_available = 1;
  }
  os_delay(1);
}
}

task2
{
int a_task, b_task, c_task;    

while(1)
{
  if (peri_data_available)
  {
    mutex_lock();        
    a_task = a;
    b_task = b;
    c_task = c;
    mutex_unlock();
    peri_data_available = 0;            
  }
  os_delay(1);
}
}

Изменено пользователем Rev0.0

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

покритикуйте, пожалуйста.
А почему вторая задача должна просыпаться каждый тик и проверять переменную? Неужели в вашей ОС нет средства сигнализации вроде флага? Чтобы вторая задача ушла в ожидание этого флага и проснулась только тогда, когда первая задача этот флаг просигналит?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот так надо делать:

 

int a,b,c;
int buf[3];

task1
{

while(1)
{
   mutex_lock();
  if (wait_peri_data(&buf))
  {
    a = buf[0];
    b = buf[1];
    c = buf[2];
  }
  mutex_unlock();
  os_delay(1);
}
}

task2
{
int a_task, b_task, c_task;    

while(1)
{
  mutex_lock();        
  a_task = a;
  b_task = b;
  c_task = c;
  mutex_unlock();
  os_delay(1);
}
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот так надо делать:
И что мы получили? Высокоприоритетная задача вынуждена ждать, пока низкоприоритетная задача отпустит mutex, хотя этот mutex ей для возобновления работы не нужен совсем. Тогда уж запихнуть все тело второй задачи в первую, эффект будет таким же, плюс экономия на стеке второй задачи и времени на переключение между ними.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

И что мы получили? Высокоприоритетная задача вынуждена ждать, пока низкоприоритетная задача отпустит mutex, хотя этот mutex ей для возобновления работы не нужен совсем. Тогда уж запихнуть все тело второй задачи в первую, эффект будет таким же, плюс экономия на стеке второй задачи и времени на переключение между ними.

 

Тут как ни крути абсурд будет по причине os_delay в обоих задачах.

А я просто привел к виду принятому в RTOS.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Сергей Борщ

Ах да, забыл сказать, что task2 нельзя делать ожидающей, задумано так, что она должна работать каждый тик, поэтому лишние переключения нежелательны.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

Самый ресурсоемкий в ваших примерах это вызов os_delay. На все остальное после этого можно не обращать внимания.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Самый ресурсоемкий в ваших примерах это вызов os_delay. На все остальное после этого можно не обращать внимания.

В смысле? Если мне нужно чтобы каждая задача выполнялась периодически, одна с периодом 1 мс, другая 10 мс, третья 100 мс, все задачи с разным приоритетом. Как вы это сделаете? os_delay как раз и нужна для того, чтобы когда высокоприоритетная задача отработала другие имели возможность тоже сделать это. Иначе вся ОС без os_delay будет работать в бесконечном цикле только в одной задаче с самым высоким приоритетом.

Изменено пользователем Rev0.0

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ах да, забыл сказать, что task2 нельзя делать ожидающей, задумано так, что она должна работать каждый тик, поэтому лишние переключения нежелательны.
Недопонял. Ну и что, что каждый тик? Все равно выполняя свой os_delay() вы вызываете переключение. Вот только момент возврата из os_delay() и начала полезной работы у вас получился никак не связан с моментом готовности данных.

 

Также нежелательно юзать мьютексы каждый раз при входе в задачу, т.к. они довольно ресурсоемки да и задачи выполняют еще какой-то код помимо кода обмена данными.
Опять не вижу связи. Если вы используете мутех при входе в задачу - задача будет ждать, даже если этот мутех ей сейчас нафиг не нужен. Я не знаю, что у вас за ОС, но мне кажется, что тут нужен не мутех, тут нужно оформить a, b, c в виде сообщения и это сообщение послать задаче 2. Возможно через очередь сообщений, а возможно достаточно и вырожденной очереди из одного сообщения. И задача 2 должна ждать появления этого сообщения. Возможно ожидать с таймаутом в один тик. А возможно надо разделить на две задачи ту работу, которую задача2 выполняет по приходу данных и ту работу, которую она должна выполнять периодически.

 

В смысле? Если мне нужно чтобы каждая задача выполнялась периодически, одна с периодом 1 мс, другая 10 мс, третья 100 мс, все задачи с разным приоритетом. Как вы это сделаете?
В вашей реализации одна задача будет выполняться с периодом 1 мс + некоторое время на ее выполнение + некоторое время на выполнение других задач, вторая - с периодом 10 мс + некоторое время, третья - 100 мс + некоторое заранее неизвестное время.

 

Иначе вся ОС без os_delay будет работать в бесконечном цикле только в одной задаче с самым высоким приоритетом.
Значит у вас что-то не так с архитектурой программы. Потому что правильно спроектированная программа с ОС все свободное время крутится в цикле задачи с наименьшим приоритетом (IdleTask), а все остальные задачи в свободное время ждут появления запускающего их события.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Может быть я задачу как-то непонятно описал.

Я же просто привел вариант когда нужно в задаче выполнять периодически действия - как вы это сделаете без os_delay? Аппаратный таймер, который шлет задаче семафор? Да, можно, но вопрос не в этом.

Как вы строите архитектуру с RTOS - все задачи чего-то ждут? У меня в проекте 10 задач и некоторые ждут, а некоторые периодически выполняются. Тем задачам, что выполняются периодически нужна связь с ожидающими. Про очередь - хорошая идея, но как я писал выше мне нужны только последние данные - нет смысла в очереди. Тем более мне нужна очередь с нулевым временем ожидания.

 

Кстати для решения того, что вы описали с нечетким периодом выполнения задач в FreeRTOS, например, есть vTaskDelayUntil.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я же просто привел вариант когда нужно в задаче выполнять периодически действия - как вы это сделаете без os_delay?

 

Используются сервисы ожидания с таймаутом.

xEventGroupWaitBits с указанием xTicksToWait

 

Но лучше создать отдельную задачу для строго периодических действий.

 

Кстати, во Free RTOS хорошо написано почему vTaskDelay нельзя использовать для периодических задач.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Имеем некоторую RTOS и 2 задачи для простоты.

Задача task1 имеет приоритет выше, чем task2. Задача task1 висит в ожидании данных от некоторой периферии.

Задача task2 должна принимать ПОСЛЕДНИЕ данные от task1, т.е если задача task1 получила скажем 10 буферов данных, то нам нужен в task2 только последний буфер, остальные можем безболезненно потерять (т.е. очередь не нужна).

Важное условие - код task2 должен выполняться как можно быстрее, поэтому я мьютекс поместил внутрь опроса флага, т.е. чтобы данные блокировались только когда они пришли, а не каждый цикл выполнения task2.

Прав ли я с таким кодом, покритикуйте, пожалуйста.

Циклический опрос следует применять только в исключительных ситуациях. В вашем случае task2 может непрерывно спать до пробуждения со стороны task1 в момент поступления новых данных. Для пробуждения можно в завимости от OS и ситуации использовать mailbox, semaphore, event и т.п.

И, похоже, вы хотите выкидывать часть входных данных, если они поступают слишком быстро(более одного сэмпла в миллисекунду), но не объявили эту цель в явном виде, и не описали условие выкидывания.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Приветствую.

 

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

 

Ну и я бы еще флаги менял по-другому. И можно без мьютексов.

 

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

Имеем некоторую RTOS и 2 задачи для простоты.

Задача task1 имеет приоритет выше, чем task2. Задача task1 висит в ожидании данных от некоторой периферии.

Задача task2 должна принимать ПОСЛЕДНИЕ данные от task1, т.е если задача task1 получила скажем 10 буферов данных, то нам нужен в task2 только последний буфер, остальные можем безболезненно потерять (т.е. очередь не нужна).

 

volatile int a,b,c;
int buf[3];
volatile int peri_data_available;

task1
{
while(1)
{
  if (wait_peri_data(&buf))
  {
    a = buf[0]; b = buf[1]; c = buf[2];
    peri_data_available = 1;
  }
  os_delay(1);
}
}

task2
{
int a_task, b_task, c_task;    

while(1)
{
  if ( peri_data_available )
  {
    do {
        peri_data_available = 0;
        a_task = a; b_task = b; c_task = c;
    }  while (peri_data_available);
    usefull_work( a_task, b_task, c_task );
  }
  os_delay(10);
}
}

 

Илья

Изменено пользователем 501-q

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

501-q, зачем вы указали цикл в task2, мне же нужны только последние данные цикл не нужен. Да и как без мьютексов, блок из трех переменных - единый, а в вашем коде задача может прервать операцию присваивания на одной из переменных и целостности блока не будет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Приветствую!

 

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

Именно для целостности блока переменных нужен цикл. Если в процессе копирования переменных в task2 сработает task1 и обновит общие переменные, то в task2 повторим копирование переменных. Обычно будет только одна итерация цикла. Поэтому и мьютексы не нужны.

 

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

 

Илья

Изменено пользователем 501-q

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...