Маленький таскер (критика)

roman2712@mail.ru
Offline
Зарегистрирован: 16.01.2014

Здравствуйте коллеги

В одной из задач понадобилось паралельное выполнение нескольких задач. 

Сответвенно, изобрел свой велосипед. Ключевой особенностью велосипеда - возможность самоубийства задачи и динамическое добавление задач.

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



class task
{
  public:
    task *next;
    void (*func)();
    uint8_t st = 1;
    task(void (*f)())
    {
      func = f;
    }
};
 
class taskManager
{
  public:
    task *current = nullptr;
    task *prev = nullptr;
 
    void add(void (*f)())
    {
      if (current == nullptr) //создание первого элемента
      {
        current = new task(f);
        prev = current;
        current->next = current;
      }
      else //создание последующих
      {
        task *temp = new task(f);
        temp->next = current;
        prev->next = temp;
      }
    }
 
    void exec()
    {
      if (current == nullptr) return;
 
      if (current->st == 0)
      {
        if (current != current->next) //удаление элемента
        {
          task *temp = current;
          prev->next = current->next;
          current = current->next;
          delete temp;
        }
        else //удаление последнего элемента
        {
          delete current;
          current = nullptr;
        }
        return;
      }
 
      current->func();
      prev = current;
      current = current->next;
    }
 
    void del()
    {
      current->st = 0;
    }
};
 
taskManager taskManager;
 
void f1()
{
  Serial.println("f1");
  static uint8_t i;
  if (i == 9) taskManager.del();
  ++i;
}
 
void f2()
{
  Serial.println("f2");
  static uint8_t i;
  if (i == 3) taskManager.del();
  ++i;
}
 
void f3()
{
  Serial.println("f3");
  static uint8_t i;
  if (i == 1) {
    taskManager.add(f4);
    taskManager.del();
  }
  ++i;
}
 
void f4()
{
  Serial.println("f4");
  static uint8_t i;
  if (i == 10) {
    taskManager.del();
  }
  ++i;
}
 
void setup()
{
  Serial.begin(9600);
  taskManager.add(f1);
  taskManager.add(f2);
  taskManager.add(f3);
}
 
void  loop()
{
  taskManager.exec();
  delay(100);
  Serial.println("step");
}

 

roman2712@mail.ru
Offline
Зарегистрирован: 16.01.2014

Админы, поправьте плиз втавку кода, тут на форуме 1 сообщение не редактируется....

roman2712@mail.ru
Offline
Зарегистрирован: 16.01.2014

Здравствуйте коллеги

В одной из задач понадобилось паралельное выполнение нескольких задач. 

Сответвенно, изобрел свой велосипед. Ключевой особенностью велосипеда - возможность самоубийства задачи и динамическое добавление задач.

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

class task
{
  public:
    task *next;
    void (*func)();
    uint8_t st = 1;
    task(void (*f)())
    {
      func = f;
    }
};

class taskManager
{
  public:
    task *current = nullptr;
    task *prev = nullptr;

    void add(void (*f)())
    {
      if (current == nullptr) //создание первого элемента
      {
        current = new task(f);
        prev = current;
        current->next = current;
      }
      else //создание последующих
      {
        task *temp = new task(f);
        temp->next = current;
        prev->next = temp;
      }
    }

    void exec()
    {
      if (current == nullptr) return;

      if (current->st == 0)
      {
        if (current != current->next) //удаление элемента
        {
          task *temp = current;
          prev->next = current->next;
          current = current->next;
          delete temp;
        }
        else //удаление последнего элемента
        {
          delete current;
          current = nullptr;
        }
        return;
      }

      current->func();
      prev = current;
      current = current->next;
    }

    void del()
    {
      current->st = 0;
    }
};

taskManager taskManager;

void f1()
{
  Serial.println("f1");
  static uint8_t i;
  if (i == 9) taskManager.del();
  ++i;
}

void f2()
{
  Serial.println("f2");
  static uint8_t i;
  if (i == 3) taskManager.del();
  ++i;
}

void f3()
{
  Serial.println("f3");
  static uint8_t i;
  if (i == 1) {
    taskManager.add(f4);
    taskManager.del();
  }
  ++i;
}

void f4()
{
  Serial.println("f4");
  static uint8_t i;
  if (i == 10) {
    taskManager.del();
  }
  ++i;
}

void setup()
{
  Serial.begin(9600);
  taskManager.add(f1);
  taskManager.add(f2);
  taskManager.add(f3);
}

void  loop()
{
  taskManager.exec();
  delay(100);
  Serial.println("step");
}

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

мне кажеца, или задачи отработают последовательно, по мере добавления?  О параллельности тут речи не идёт совсем. 

roman2712@mail.ru
Offline
Зарегистрирован: 16.01.2014

последовательно. по очереди с помощью связанного списка. 1 запуск метода exec - 1 задача. и так по кругу.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ну а параллельность тут причём?  из #1 сообщения

b707
Offline
Зарегистрирован: 26.05.2017

roman2712@mail.ru пишет:

последовательно. по очереди с помощью связанного списка. 1 запуск метода exec - 1 задача. и так по кругу.

а смысл? :)

обыкновенный loop() делает то же самое, только код раз в 20 короче:

void loop()
{
  Serial.begin(9600);
  f1();
  f2();
  f3();
}

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

вот и я про то же. :) 

sadman41
Offline
Зарегистрирован: 19.10.2016

b707 пишет:

а смысл? :)

обыкновенный loop() делает то же самое, только код раз в 20 короче:

Только квону так не пиши...

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

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

kalapanga
Offline
Зарегистрирован: 23.10.2016

Ну, тут (в loop) список фиксированный, а у товарища динамический! Но куда это применить - мне не придумывается.

Может автор намекнёт, что за задачу решал?

sadman41
Offline
Зарегистрирован: 19.10.2016

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

roman2712@mail.ru
Offline
Зарегистрирован: 16.01.2014

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

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

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

b707
Offline
Зарегистрирован: 26.05.2017

kalapanga пишет:

Ну, тут (в loop) список фиксированный, а у товарища динамический!

он него динамический только потенциально, если в его класс добавить методы изменения порядка выполнения задач по каким-либо признакам. А так как сделано сейчас - от моего списка в ЛУП не отличается ничем :)

kalapanga
Offline
Зарегистрирован: 23.10.2016

Ну в примере-то да, динамики никакой. Вот я поэтому про задачу и спросил - вдруг оно и правда для чего-то нужно!

b707
Offline
Зарегистрирован: 26.05.2017

roman2712@mail.ru пишет:

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

в варианте с ЛУП функции точно так же внутри могут быть стейт-машинами ровно с теми же возможностями...

Я бы сказал. что в Вашей реализации полезное зерно - только в единообразном оформлении задач как членов одного класса. А сам "таскер" в том виде, как сейчас - имхо, вещь бесполезная

 

roman2712@mail.ru
Offline
Зарегистрирован: 16.01.2014

Для функций действительно это можно и в лупе, только тогда дополнительно для каждой f() надо будет делать влаг запуска \ останова. 

А вот если у нас вместо функции будут классы инициируемые во время работы программы, то просто в лупе это не проканает... В данном случае f() как пример, не более того. 

Меня на самом деле интересует вопрос не как сделать проще (это я и сам могу), а не пропустил ли я чего нить при работе с указателями...

Проще говоря - возможна ли тут утечки памяти ?

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Канешна.

Logik
Offline
Зарегистрирован: 05.08.2014

roman2712@mail.ru пишет:

Для функций действительно это можно и в лупе, только тогда дополнительно для каждой f() надо будет делать влаг запуска \ останова. 

А вот если у нас вместо функции будут классы инициируемые во время работы программы, то просто в лупе это не проканает...

 

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

roman2712@mail.ru пишет:

Меня на самом деле интересует вопрос не как сделать проще (это я и сам могу), а не пропустил ли я чего нить при работе с указателями...

 

Ну как - чего?! Как всегда проверку успешности выделения памяти и реакцию на неуспешность ;)

roman2712@mail.ru
Offline
Зарегистрирован: 16.01.2014

Ну как бы так и сделано...

Ну проверку надо бы :) тока не понятно куда экстеншен кинуть... хотя...  

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Мошт, exeption?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Достатошно NULL вернуть.

Logik
Offline
Зарегистрирован: 05.08.2014

//Мошт, exeption?

угу. Тока всеравно ж некуда.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Просто ТС путает какойта extention и exeption. Вот я и спрашываю. Мошт, он и не подозревает, што некуда, и единственная функция, которая ему поможет, это abort(), другой нету. ¦(

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

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

Logik
Offline
Зарегистрирован: 05.08.2014

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

sadman41
Offline
Зарегистрирован: 19.10.2016

DetSimen пишет:

Просто ТС путает какойта extention и exeption. Вот я и спрашываю. Мошт, он и не подозревает, што некуда, и единственная функция, которая ему поможет, это abort(), другой нету. ¦(

У меня в телефонной станции есть extentions... может он телефонист?

roman2712@mail.ru
Offline
Зарегистрирован: 16.01.2014

отвлекли, слово перепутал ))) и нет, не телефонист )

roman2712@mail.ru
Offline
Зарегистрирован: 16.01.2014

ИМХО В посте 167 есть неопределенное поведение после самоубийства задачи из середины списка. та компоновка, которая в примере, эту проблему не выявляет, так как фактически в пуле задач присутсвует только одна
задача...
и 2 момент. вроде как после delete указатель не становиться равным NULL, хотя вроде как должен. это я на своем коде очень хорошо понял...

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

roman2712@ma, я не хочу жевать эту проблему. Приводить кучу доводов и ход мысли. Потому чтомне сейчас не надо. Может потом позже напишу или скорее не напишу.