Przełączanie zadań w programie OUX / C+
- Janusz Augustyński
- 22 kwi 2020
- 3 minut(y) czytania
Zaktualizowano: 3 maj 2020
Podsystem OUX zawiera wbudowaną obsługę zadań wewnętrznych programu pisanego w technologii OUX / języku C+. Zadania są podobne do tzw. wątków (threads), ale nie wymagają obsługi (zewnętrznej) przez środowisko systemu operacyjnego. Ponadto zadania podpadają pod inny model użycia oraz są zintegrowane z raportami, cyklerami i impulsatorami.
W każdym czasie podczas wykonywania się programu napisanego w technologii OUX / języku C+ aktywne jest tylko jedno zadanie, tzn. podsystem OUX używa kooperacyjnego przełączania zadań. Zapewnia to szybkość wykonania programu i brak rywalizacji o zasoby.
Zadania
Pierwszym zadaniem, które nie musi być odrębnie definiowane, jest to, które wykonuje się przy starcie programu procedurą main. Inne zadania potrzebują być zdefiniowane jako procedura ogólnie w następujący sposób:
D( moduł, nazwa_zadania )
{
I_D
{
}
}
Gdzie I_D to jest pętla główna zadania wykonywana aż do żądania przez system jego zakończenia, gdy jest wyrzucane (czyli zatrzymywane) z innego miejsca programu. Tak zdefiniowane zadania tworzy się (uruchamia) następującą instrukcją:
D_M( moduł, nazwa_zadania );
A wyrzuca się (zatrzymuje) instrukcją:
D_W( moduł, nazwa_zadania );
W pętli głównej zadania musi znajdować się co najmniej (i zwykle) jedna instrukcja przełączenia do innego zadania (wybranego automatycznie przez podsystem OUX).
Taką instrukcją jest na przykład instrukcja bezwarunkowego przełączenia I_B jak w przykładzie:
D( moduł, nazwa_zadania )
{
I_D
{ I_B()
break;
}
}
Instrukcja ta (podobnie jak każda instrukcja przełączenia) zawiera blok wykonywany wtedy, gdy zadanie ma być wyrzucone. Tutaj następuje zwykłe opuszczenie pętli głównej I_D zadania bez wykonywania jakichkolwiek innych działań. Jakkolwiek na żądanie zakończenia zadania zawsze musi nastąpić opuszczenie tej pętli w jakikolwiek sposób.
Instrukcja bezwarunkowego przełączenia powoduje, że zadanie jest wykonywane przez cały czas, chyba że w tym zadaniu znajdzie się jakiekolwiek oczekiwanie na coś. Czyli by nie marnować czasu procesora na bezustanne wykonywanie zadania należy użyć instrukcji warunkowego przełączenia zadania: w oczekiwaniu na raport albo na określony czas cyklera lub impulsatora.
Raporty
Raport jest to taki obiekt, który sygnalizuje zaistnienie czegoś. Zadanie może czekać na nazwany raport. Raport jest tworzony i wyrzucany przez zadanie, które na niego czeka, jak w przykładzie:
D( moduł, nazwa_zadania )
{ X_M( moduł, nazwa_raportu );
I_D
{ X_B( moduł, nazwa_raportu, 0 )
break;
}
X_W( moduł, nazwa_raportu );
}
Instrukcja X_M tworzy raport, a instrukcja X_W wyrzuca go.
X_B jest instrukcją warunkowego przełączenia zadania w oczekiwaniu na raport. Jeśli nadejdzie tak nazwany raport, to zadanie będzie wznowione od tej instrukcji.
Instrukcja ta zawiera trzeci parametr, gdzie można podać wskaźnik zmiennej typu N, by zachowana została w niej liczba zgubionych tak nazwanych raportów przed wznowieniem tego zadania.
W procedurze, w której raport będzie emitowany, deklaruje się go instrukcją X_A jak w przykładzie:
X_A( moduł, nazwa_raportu );
X_F( moduł, nazwa_raportu );
Instrukcja X_F emituje raport.
Znaczniki stanu
Jakkolwiek wygodniej jest deklarować stany programu, które decydują o emisji bądź nieemisji nazwanego raportu.
W trakcie przetwarzania danych pojawiają się stany, które decydują, że raport powinien być wyemitowany bądź nie. Wtedy należy użyć znaczników stanu. Znaczniki stanu są zwykle nazywane tak samo jak raport, o którym decydują. Na przykład:
B U_L( moduł, nazwa_raportu );
if()
U_F( moduł, nazwa_raportu );
if()
U_L( moduł, nazwa_raportu );
X_U( moduł, nazwa_raportu );
Instrukcja U_L ustawia znacznik stanu na stan wyłączony, a w powyższym przykładzie jest użyta łącznie z deklaracją zmiennej typu B. Instrukcja U_F ustawia znacznik na stan włączony. Natomiast instrukcja X_U emituje raport, jeśli tak samo nazwany znacznik jest włączony.
Istnieje również instrukcja U_R, która czyta stan znacznika, oraz instrukcja U_E, która czyta go, a następnie ustawia na wyłączony. Na przykład:
if( U_R( moduł, nazwa_raportu ))
;
if( U_E( moduł, nazwa_raportu ))
;
Znaczniki stanu można używać nie tylko do warunkowego emitowania raportu, ale również do oznaczania stanów cząstkowych w obiektach, stanów zapisywanych w postaci pól bitowych jak w przykładzie:
struct
{ unsigned U_R( state, visible ) :1;
}q;
U_L( q.state, visible );
U_F( q.state, visible );
Do tego celu wymyślone zostały standardowe nazwy takie jak:
state dla stanu obiektu
mode dla wyjątkowego, tymczasowego trybu pracy obiektu
req dla żądania realizacji funkcji
_ex jako dopisek, np. do mode, jako rozszerzenie trybu
Cyklery
Cykler wylicza czas, z którym został utworzony. Po wyliczeniu całego czasu instrukcja Y_B warunkowego przełączenia zadania w oczekiwaniu na czas cyklera wznawia zadanie od tej instrukcji i wylicza czas od nowa. Na przykład:
D( moduł, nazwa_zadania )
{ I timer = Y_M(czas);
I_D
{ Y_B( timer, 0 )
break;
}
Y_W(timer);
}
Instrukcja Y_M tworzy cykler z czasem podanym milisekundach, a instrukcja Y_W wyrzuca go.
Instrukcja Y_B zawiera drugi parametr, gdzie można podać wskaźnik zmiennej typu N, by zachowana została w niej liczba zgubionych upływów czasów przed wznowieniem tego zadania.
Cykler służy do synchronizowania czasu pracy zadania, gdy jest to wyraźnie wymagane, na przykład w programie sekundnika zegara, a nie do losowego spowalniania programu.
Impulsatory
Impulsator jest połączeniem raportu z cyklerem: wylicza czas, z którym został wyemitowany, i wtedy wznawia zadanie od instrukcji Yi_B warunkowego przełączenia zadania w oczekiwaniu na czas impulsatora. Na przykład:
D( moduł, nazwa_zadania )
{ Yi_M( moduł, nazwa_impulsatora );
I_D
{ Yi_B( moduł, nazwa_impulsatora );
break;
}
Yi_W( moduł, nazwa_impulsatora );
}
Instrukcja Yi_M tworzy impulsator, a instrukcja Yi_W wyrzuca go.
Natomiast przykład emisji impulsatora jest następujący:
Yi_A( moduł, nazwa_impulsatora );
if()
Yi_F( moduł, nazwa_impulsatora, czas );
if()
Yi_L( moduł, nazwa_impulsatora );
Instrukcja Yi_F emituje impulsator po czasie podanym w milisekundach. Instrukcja Yi_L usuwa emisję impulsatora.
Comentários