lekcja! 3MU5LZRREFNSCJXNEIEB6XXSI3UZJ6S4OO6CTYY


LEKCJA 21

KILKA PROCESÓW JEDNOCZEŚNIE.

W trakcie tej lekcji dowiesz się, jak to zrobić, by Twój PC mógł wykonywać kilka rzeczy jednocześnie.

Procesy współbieżne.

Sprzęt, czyli PC ma możliwości zdecydowanie pozwalające na techniczną realizację pracy wielozadaniowej. Nie ma też żadnych przeciwskazań, by zamiast koprocesora umożliwić w PC instalację drugiego (trzeciego) równoległego procesora i uprawiać na PC poważne programowanie współbieżne. Po co? To proste. Wyobraź sobie Czytelniku, że masz procesor pracujący z częstotliwością 25 MHz (to 25 MILIONÓW elementarnych operacji na sekundę!). Nawet, jeśli wziąć pod uwagę, że niektóre operacje (dodawanie, mnożenie, itp.) wymagają wielu cykli - i tak można w uproszczeniu przyjąć, że Twój procesor mógłby wykonać od kilkuset tysięcy do kilku milionów operacji w ciągu sekundy. Jeśli pracujesz np. z edytorem tekstu i piszesz jakiś tekst - znacznie ponad 99% czasu Twój procesor czeka KOMPLETNIE BEZCZYNNIE (!) na naciśnięcie klawisza. Przecież Twój komputer mogłby w tym samym czasie np. i formatować dyskietkę (dyskietka też jest powolna), i przeprowadzać kompilację programu, i drukować dokumenty, i przeprowadzić defragmentację drugiego dysku logicznego, itp. itd.. Nawet taka pseudowspółbieżność realizowana przez DOS, Windows, czy sieć jest ofertą dostatecznie atrakcyjną, by warto było przyjrzeć się mechanizmom PSEUDO-współbieżności w C i C++. Współbieżność procesów, może być realizowana na poziomie

* sprzętowym (architektura wieloprocesorowa),

* systemowym (np. Unix, OS/2),

* nakładki (np. sieciowej - time sharing, token passing)

* aplikacji (podział czasu procesora pomiędzy różne funkcje/moduły tego samego pojedynczego programu).

My zajmiemy się tu współbieżnością widzianą z poziomu aplikacji. Funkcje setjmp() (ang. SET JuMP buffer - ustaw bufor umożliwiający skok do innego procesu) i longjmp() (ang. LONG JuMP - długi skok - poza moduł) wchodzą w skład standardu C i w związku z tym zostały "przeniesine" do wszystkich kompilatorów C++ (nie tylko Borlanada).

Porozmawiajmy o narzędziach.

Zaczniemy od klasycznego zestawu narzędzi oferowanego przez Borlanda. Aby zapamiętać stan przerwanego procesu stosowana jest w C/C++ struktura PSS (ang. Program Status Structure) o nazwie jmp_buf (JuMP BUFfer - bufor skoku). W przypadku współbieżności wielu procesów (więcej niż dwa) stosuje się tablicę złożoną ze struktur typu

struct jmp_buf TablicaBuforow[n];

Struktura służy do przechowywania informacji o stanie procesu (rejestrach procesora w danym momencie) i jest predefiniowana w pliku SETJMP.H:

typedef struct

{

unsigned j_sp, j_ss, j_flag, j_cs;

unsigned j_ip, j_bp, j_di, j_es;

unsigned j_si, j_ds;

} jmb_buf[1];

Prototypy funkcji:

int setjmp(jmp_buf bufor);

void longjmp(jmp_buf bufor, int liczba);

W obu przypadkach jmp_buf bufor oznacza ten sam typ bufora (niekoniecznie ten sam bufor - może ich być wiele), natomiast int liczba oznacza tzw. return value - wartość zwracaną po powrocie z danego procesu. Liczba ta może zawierać informację, z którego procesu nastąpił powrót (lub inną przydatną w programie), ale nie może być ZEREM. Jeśli funkcja longjmp() otrzyma argument int liczba == 0 - zwróci do programu wartość 1.

Wartość całkowita zwracana przez funkcję setjmp() przy pierwszym wywołaniu jest zawsze ZERO a przy następnych wywołaniach (po powrocie z procesu) jest równa parametrowi "int liczba" przekazanemu do ostatnio wywołanej funkcji longjmp(). Przyjrzyjmy się temu mechanizmowi w praktyce. Wyobraźmy sobie, że chcemy realizować współbieżnie dwa procesy - proces1 i proces2. Proces pierwszy będzie naśladował w uproszczeniu wymieniony wyżej edytor tekstu - pozwoli na wprowadzanie tekstu, który będzie powtarzany na ekranie. Proces drugi będzie przesuwał w dolnej części ekranu swój numerek - cyferkę 2 (tylko po to, by było widać, że działa). Program główny wywołujący oba procesy powinien wyglądać tak:

...

void proces1(void);

void proces2(void);

int main(void)

{

clrscr();

proces1();

proces2();

return 0;

}

Ależ tu nie ma żadnej współbieżności! Oczywiście. Aby zrealizować współbieżność musimy zadeklarować bufor na bieżący stan rejestrów i zastosować funkcje setjmp():

#include <setjmp.h>

void proces1(void);

void proces2(void);

jmp_buf bufor1;

int main(void)

{

clrscr();

if(setjmp(bufor1) != 0) proces1(); //Powrót z procesu2 był?

proces2();

return 0;

}

Po wywołaniu funkcji setjmp() zostanie utworzony bufor1, w którym zostanie zapamiętany stan programu. Funkcja, jak zawsze przy pierwszym wywołaniu zwróci wartość ZERO, więc warunek

if(setjmp(bufor1) != 0) ...

nie będzie spełniony i proces1() nie zostanie wywołany. Program pójdzie sobie dalej i uruchomi proces2():

void proces2(void)

{

for(;;)

{

gotoxy(10,20);

printf("PROCES 2: ");

for(int i = 1; i<40; i++)

{

printf(".2\b");

delay(5); //UWAGA: delay() tylko dla DOS!

}

longjmp(bufor1, 1); <--- wróć

} ____________ tę jedynkę zwróci setjmp()

}

Proces 2 będzie drukował "biegającą dwójkę" (zwolnioną przez opóźnienie delay(5); o pięć milisekund), poczym funkcja longjmp() każe wrócić z procesu do programu głównego w to miejsce:

int main(void)

{

clrscr();

if(setjmp(bufor1)) proces1(); <--- tu powrót

proces2();

return 0;

}

Zmieni się tylko tyle, że powtórnie wywołana funkcja setjmp() zwróci tym razem wartość 1, zatem warunek będzie spełniony i rozpocznie się proces1():

void proces1(void)

{

while(kbhit())

{

gotoxy(1,1);

printf("PROCES1, Pisz tekst: [Kropka - Koniec]");

gotoxy(pozycja,2);

znak = getch();

printf("%c", znak);

pozycja++;

}

if(znak == '.') exit (0);

}

Proces 1 sprawdzi przy pomocy funkcji kbhit() czy w buforze klawiatury oczekuje znak (czy coś napisałeś). Jeśli tak - wydrukuje znak, jeśli nie - zakończy się i program przejdzie do procesu drugiego. A oto program w całości:

[P075.CPP]

#include <stdio.h>

#include <process.h>

#include <setjmp.h>

#include <conio.h>

#include <dos.h>

void proces1(void);

void proces2(void);

jmp_buf bufor1, bufor2;

char znak;

int pozycja = 1;

int main(void)

{

clrscr();

if(setjmp(bufor1)) proces1();

proces2();

return 0;

}

void proces1(void)

{

while(kbhit())

{

gotoxy(1,1);

printf("PROCES1, Pisz tekst: [Kropka - Koniec]");

gotoxy(pozycja,2);

znak = getch();

printf("%c", znak);

pozycja++;

}

if(znak == '.') exit (0);

}

void proces2(void)

{

for(;;)

{

gotoxy(10,20);

printf("PROCES 2: ");

for(int i = 1; i<40; i++)

{

printf(".1\b");

delay(5);

}

longjmp(bufor1,1);

}

}

[!!!] UWAGA

Funkcja delay() użyta dla opóżnienia i zwolnienia procesów

będzie funkcjonować tylko w środowisku DOS. Przy uruchamianiu prykładowego programu pod Windows przy pomocy BCW należy tę funkcję poprzedzić znakiem komentzrza // .

Wyobrażmy sobie, że mamy trzy procesy. Przykład współbieżności trzech procesów oparty na tej samej zasadzie zawiera program poniżej

[P076.CPP]

#include <stdio.h>

#include <process.h>

#include <setjmp.h>

#include <conio.h>

#include <dos.h>

void proces1(void);

void proces2(void);

void proces3(void);

jmp_buf bufor1, bufor2;

char znak;

int pozycja = 1;

int main(void)

{

clrscr();

if(setjmp(bufor1)) proces1();

if(setjmp(bufor2)) proces2();

proces3();

return 0;

}

void proces1(void)

{

while(kbhit())

{

gotoxy(1,1);

printf("PROCES1, Pisz tekst: [Kropka - Koniec]");

gotoxy(pozycja,2);

znak = getch();

printf("%c", znak);

pozycja++;

}

if(znak == '.') exit (0);

}

void proces2(void)

{

for(;;)

{

gotoxy(10,20);

printf("PROCES 2: ");

for(int i = 1; i<40; i++)

{

printf(".2\b");

delay(5);

}

longjmp(bufor1, 1);

}

}

void proces3(void)

{

for(;;)

{

gotoxy(10,23);

printf("PROCES 3: ");

for(int i = 1; i<40; i++)

{

printf(".3\b");

delay(2);

}

longjmp(bufor2,2);

}

}

Procesy odbywają się z różną prędkością. Kolejność uruchamiania procesów będzie:

- proces3()

- proces2()

- proces1()

Po uruchomieniu programu zauważysz, że proces pierwszy (pisania) został spowolniony. Można jednak temu zaradzić przez ustawienie flag i priorytetów. Jeśli dla przykładu uważamy, że pisanie jest ważniejsze, możemy wykrywać zdarzenie - naciśnięcie klawisza w każdym z mniej ważnych procesów i przerywać wtedy procesy mniej ważne. Wprowadzanie tekstu w przykładzie poniżej nie będzie spowolnione przez pozostałe procesy.

[P077.CPP]

#include <stdio.h>

#include <process.h>

#include <setjmp.h>

#include <conio.h>

#include <dos.h>

void proces1(void);

void proces2(void);

void proces3(void);

jmp_buf BuforStanu_1, BuforStanu_2;

char znak;

int pozycja = 1;

int main(void)

{

clrscr();

if(setjmp(BuforStanu_1)) proces1();

if(setjmp(BuforStanu_2)) proces2();

proces3();

return 0;

}

void proces1(void)

{

while(kbhit())

{

gotoxy(1,1);

printf("PROCES1, Pisz tekst: [Kropka - Koniec]");

gotoxy(pozycja,2);

znak = getch();

printf("%c", znak);

pozycja++;

}

if(znak == '.') exit (0);

}

void proces2(void)

{

for(;;)

{

gotoxy(10,20);

printf("PROCES 2: ");

for(int i = 1; i<40; i++)

{

if(kbhit()) break;

printf(".2\b");

delay(5);

}

longjmp(BuforStanu_1, 1);

}

}

void proces3(void)

{

for(;;)

{

gotoxy(10,23);

printf("PROCES 3: ");

for(int i = 1; i<40; i++)

{

if(kbhit()) break;

printf(".3\b");

delay(2);

}

longjmp(BuforStanu_2,2);

}

}

[!!!]UWAGA

W pierwszych dwu przykładach trzymanie stale wciśniętego klawisa spowoduje tylko automatyczną repetycję wprowadzanego znaku. W przykładzie trzecim spowoduje to przerwanie procesów 2 i 3, co będzie wyraźnie widoczne na monitorze (DOS). Zwróć uwagę, że kbhit() nie zmienia stanu bufora klawiatury.

W bardziej rozbudowanych programach można w oparciu o drugi parametr funkcji longjmp() zwracany przez funkcję setjmp(buf) po powrocie z procesu identyfikować - z którego procesu nastąpił powrót i podejmować stosowną decyzję np. przy pomocy instrukcji switch:

switch(setjmp(bufor))

{

case 1 : proces2();

case 2 : proces3();

.....

default : proces0();

}

[!!!]UWAGA

* Zmienne sterujące przełączaniem procesów powinny być zmiennymi globalnymi, bądź statycznymi. Także dane, które nie mogą ulec nadpisaniu bezpieczniej potraktować jako globalne.

W przypadku wielu procesów celowe jest utworzenie listy, bądź kolejki procesów. Przydatny do tego celu bywa mechanizm tzw. "łańcuchowej referencji". W obiektach klasy PozycjaListy należy umieścić pole danych - strukturę i pointer do następnego procesu, któremu (zgodnie z ustalonym priorytetem) należy przekazać sterowanie:

static jmp_buf Bufor[m]; <-- m - ilość procesów

...

class PozycjaListy

{

public:

jmp_buf Bufor[n]; <-- n - Nr procesu

PozycjaListy *nastepna;

}

Wyobrażmy sobie sytuację odrobinę różną od powyższych przykładów (w której zresztą para setjmp() - longjmp() równie często występuje.

#include <setjmp.h>

jmp_buf BuforStanu;

int Nr_Bledu;

int main(void)

{

Nr_Bledu = setjmp(BuforStanu) <-- tu nastąpi powrót

if(Nr_Bledu == 0) <-- za pierwszym razem ZERO

{

/* PRZED powrotem z procesu (ów) */

....

Proces(); <-- Wywołanie procesu

}

else

{

/* PO powrocie z procesu (ów) */

ErrorHandler(); <-- obsługa błędów

}

....

return 0;

}

Taka struktura zapewnia działanie następujące:

- Był powrót z procesu?

NIE: Wywołujemy proces!

TAK: Obsługa błędów, które wystąpiły w trakcie procesu.

Jeśli teraz proces zaprojektujemy tak:

void Proces()

{

int Flaga_Error = 0;

...

/* Jeśli nastąpiły błędy, flaga w trakcie pracy procesu jest

ustawiana na wartość różną do zera */

if(Error) Flaga_Error++;

...

if(Fllaga_Error != 0) longjmp(BuforStanu, Flaga_Error);

...

}

proces przekaże sterowanie do programu w przypadku wystąpienia błędów (jednocześnie z informacją o ilości/rodzaju błędów).

[Z]

1. Napisz samodzielnie program realizujący 2, 3, 4 procesy współbieżne. Jeśli chcesz, by jednym z procesów stał się całkowivie odrębny program - skorzystaj z funkcji grupy spawn...() umożliwiających w C++ uruchamianie procesów potomnych.

1



Wyszukiwarka

Podobne podstrony:
Lekcja kliniczna 2 VI rok WL
Lekcja Przysposobienia Obronnego dla klasy pierwszej liceum ogólnokształcącego
Lekcja wychowania fizycznego jako organizacyjno metodyczna forma lekcji ruchu
Lekcja kliniczna nr 2 VI rok WL
04 Lekcja
PF7 Lekcja2
lekcja52
Printing bbjorgos lekcja41 uzupelnienie A
lekcja 18 id 265103 Nieznany
Hydrostatyka i hydrodynamika lekcja ze wspomaganiem komputerowym
Lekcja 6 Jak zapamietywac z notatki Tajemnica skutecznych notatek
lekcja 20
lekcja20
Lekcja 04 Szene 04
LINGO ROSYJSKI raz a dobrze Intensywny kurs w 30 lekcjach PDF nagrania audio audio kurs
Printing bbjorgos lekcja01 05 A
'Half Life', czyli pół życia przed monitorem zagrożenia medialne foliogramy gim modul 3 lekcja 5
Lekcja od mamy
lekcja 3 id 265134 Nieznany

więcej podobnych podstron