top of page

Przełączanie zadań w programie OUX / C+

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.

Ostatnie posty

Zobacz wszystkie
Klient HTTP/1.1 i HTTP/2

Do modułu “base” dodałem podstawową obsługę klienta HTTP/1.1 i HTTP/2, korzystając z szyfrowania TLS zapewnianego przez OpenSSL....

 
 
 

Comentários


©2024 by technologia OUX / język C+. Stworzone przy pomocy Wix.com

bottom of page