Параллельное выполнение объектных действий C++

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

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

Либо другой пример — фейерверк. Ведь, запускается не одна ракета, а несколько. И вот они взрываются, все по-разному, затем из них вырывается еще один снаряд и т.д. Вам нужно на ходу создавать/изменять/удалять объекты.

Давайте создадим структуру одного из объектов:

1
2
3
4
5
6
struct Object {
   int x;
   int y;
   unsigned char var[3];
   unsigned char type;
}

Для удобства манипуляции лучше динамически выделять под объекты память. Если объектов ~20шт, то нужно где-то хранить указатели на них. Нам нужен стек: (max=100)

1
struct Object *pointerObj[100];

Перейдем к функции создания объектов. Так как каждый объект должен выполнять свою задачу, ему нужно указать какую. Пусть будет 3типа задачи:

  1. Изменение координат.
  2. Генерация случайных чисел и запись их в массив var.
  3. Выполнять *ничего*.

Функция будет принимать 3 аргумента: координаты и тип задачи:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void createObjects(int x, int y, unsigned char type) {
   uint8_t count = 0;
   while(pointerObj[count]!=NULL) ++count; //Ищем свободное место в стеке
 
   pointerObj[count] = (struct Object*)  malloc(sizeof(struct Object)); //Выделяем память под объект
   pointerObj[count]->type = type; //Присваиваем тип
 
   switch(type) {
      case 1: //Параметры для Задача 1
         pointerObj[count]->x = 0;
         pointerObj[count]->y = 0;
      break;
 
      case 2: //…Задача 2
         pointerObj[count]->var[0] = 0;
         pointerObj[count]->var[1] = 0;
         pointerObj[count]->var[2] = 0;
      break;
 
      case 3: //…Задача 3
         asm("nop"); //Ничего
      break;
    }
}

Естественно, если мы можем создавать объекты, то нужна функция удаления объектов. У функции будет 1 аргумент — номер объекта в стеке. Конечно, можно организовать поиск по координатам и удалять по ним, но пока мы это делать не будем.

1
2
3
4
5
6
void removeObjects(uint8_t count) {
   if (pointerObj[count]!=NULL) {
      free(pointerObj[count]);
      pointerObj[count] = NULL;
   }
}

Да. Мы чуть-чуть не забыли о функции инициализации. А она будет совсем простая. Ее задача — обнулить стек.

1
2
3
void initObjects() {
   for (uint8_t i=0; i<100; ++i) pointerObj[i] = NULL;
}

Теперь давайте перейдем к самому обработчику объектов. Он должен «пробегать» по всему стеку, определять тип задачи объекта и собственно, говоря, выполнять сами задачи.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void actionsObjects() {
  uint8_t count = 0;
  while(count<100) { //Размер стека = 100
  if (pointerObj[count]!=NULL) {
   switch(pointerObj[count]->type) {
      case 1:
          pointerObj[count]->x += rand() % 8 - 16; //Изменяем координаты
          pointerObj[count]->y--;
      break;
 
      case 2:
          pointerObj[count]->var[0] = rand() % 256; //Генерируем чепухню в переменные
          pointerObj[count]->var[1] = rand() % 256;
          pointerObj[count]->var[2] = rand() % 256;
      break;
 
      case 3:
          asm("nop"); //Ничего не делаем
      break;
   }
  }
  ++count;
  }
}

Примечание: использование delay, sleep и прочего, тормозящего программу на N секунд — строго запрещается!
Вот и вся система готова, но нам последующая обработка и, возможно, вывод данных. Общая структура подпрограммы обработки стека вглядит так:

  • Открытие стека
  • Сканирование стека с условием, что ячейка стека не пуста
  • Открытие объекта из ячейки стека
  • Действия с объектом
  • Закрытие объекта из ячейки стека
  • Закрытие стека

…ну или более «человекопонятно» на C++:

1
2
3
4
5
6
7
8
9
10
11
void abrakadabra() {
   uint8_t count = 0;
   while(count<100) {
      if (pointerObj[count]!=NULL) {
         //-------
         //Действия с объектом
         //-------
      }
   ++count;
   }
}

Также хотел упомянуть, что если стек > 100, то, естественно, нужно поменять тип переменной count на uint16_t/uint32_t/uint64_t/uint65536_t. Все это за-за моей чрезмерной оптимизации и экономии.  🙄

Пример использования: (тот же, что и в предыдущих статьях :))

Все враги и прочие персонажи, управляемые с помощью программы, а также визуальные эффекты — все дело этого вышеописанного кода. Скорее всего, можно сделать всю систему лучше, оптимизировать, но мое дело донести до вас суть.

0
0

About Кирилл Васин

Прохожий из шапки сайта

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *