Professional Documents
Culture Documents
IV.
Wgb jzyka C
Programowanie wspbiene
Jzyk C nie zawiera oczywicie adnych mechanizmw umoliwiajcych programowanie wspbiene (np. takich, jak w jzyku Ada). W rozdziale niniejszym
przedstawi implementacj moduu umoliwiajcego pseudo-wspbiene wykonywanie funkcji w jzyku C. Termin "pseudo-wspbieno" jest tutaj bardzo
wany, gdy w adnym wypadku zastosowane rozwizanie nie umoliwia realizacji rzeczywistej wspbienoci na maszynach wieloprocesorowych. Pomimo tego,
dla zwikszenia czytelnoci opisu, bd w dalszej jego czci uywa terminw
"wspbieny" oraz "pseudo-wspbieny" wymiennie.
Implementacja wspomnianego moduu bdzie pretekstem do zastosowania wielu
technik opisanych w poprzednich rozdziaach tej ksiki. Dlatego te przed przystpieniem do czytania tego rozdziau polecam przeczytanie rozdziaw poprzednich. Z drugiej strony implementacja ta jest przykadem niecodziennego
stylu programowania w jzyku C, charakteryzujcego si bardzo intensywnym
uyciem preprocesora.
Wspbiene wykonywanie funkcji nie bdzie realizowane na poziomie systemu
operacyjnego lecz na poziomie programu w C, i bdzie ono wykonane
z wykorzystaniem jedynie elementw jzyka standardowego. Oznacza to midzy
innymi, e modu bdzie przenony zarwno na rne kompilatory (naley jednak ostronie stosowa opcje optymalizacji) jak i rne platformy sprztowe. Inn konsekwencj realizacji przeczania zada cakowicie na poziomie jzyka C
jest "gruboziarnisto" zrealizowanej pseudo-wspbienoci. Jeeli dwa (lub
wicej) zadania (funkcje, programy) maj by wykonywane w sposb wspbieny na jednym procesorze, to w rzeczywistoci na zmian wykonywane s
pewne mae fragmenty tych zada. Najmniejsz tak czstk moe by instrukcja procesora, ktra nie moe ju by podzielona. Takie rozwizanie byoby
pseudo-wspbienoci "drobnoziarnist" i gwarantowaoby maxymalne zudzenie rzeczywistej wspbienoci. W przedstawionym poniej module najmniejsz czci funkcji, ktra musi by wykonana, zanim sterowanie zostanie
przekazane do innej funkcji, jest jedna instrukcja (ang. statement) jzyka C.
1. Dlaczego wspbieno?
Klasyczne programy wspbiene s wykonywane na maszynach wieloprocesorowych. Celem zastosowania rwnolegych komputerw i rwnolegych programw
jest zmniejszenie zoonoci czasowej rozwizywanego zadania. Jest spraw
oczywist, e w przypadku programu wykonywanego pseudo-wspbienie na
komputerze jednoprocesorowym nie mona liczy na zwikszenie prdkoci oblicze. Co wicej, wykonanie w takim przypadku kilku zada musi trwa duej ni
trwaoby wykonanie tych zada sekwencyjnie jedno po drugim. Dzieje si tak
dlatego, e oprcz kodu zada procesor musi wykonywa pewien kod zwizany
IV Programowanie wspbiene
63
z ich przeczaniem. Mona by w takim razie powiedzie, e pseudowspbieno jest sztuk dla sztuki. Nie jest to prawd, a najlepszym na to dowodem jest popularno programw typu DESQview czy Windows, umoliwiajcych
pseudowspbiene wykonywanie programw. Twierdzenie, e wielozadaniowo
realizowana na jednym procesorze nie moe przynie zyskw czasowych jest
prawd dopty, dopki zadania cay czas wymagaj pracy procesora.
W rzeczywistoci czste s sytuacje, gdy wikszo czasu pracy zadania nie jest
zuywana na prac procesora. Na przykad operacje na pamici zewntrznej s
zwykle na tyle wolne w porwnaniu z szybkoci procesora, e mgby on rwnoczenie wykonywa inn prac. Jeszcze bardziej skrajnym przypadkiem jest czekanie przez zadanie na dane wprowadzane przez uytkownika z klawiatury. Jeeli
jedno z zada utkno w takim wskim gardle, procesor moe powici swj czas
na wykonanie innych zada. Mona to zrealizowa wanie poprzez pseudowspbieno.
Zyskiwanie czasu w takich sytuacjach nie jest jednak jedynym motywem zastosowania wielozadaniowoci. Programy wykonujce kilka zada na raz mog by
bardzo wygodne dla uytkownika. Przykadem niech bdzie edytor tekstw zapisujcy co jaki czas redagowany tekst "w tle".
Jak ju wczeniej wspomniaem, realizacja wspbienego wykonywania funkcji
bdzie polegaa na wykonywaniu na zmian kolejnych fragmentw kadej
z funkcji. Do przekazywania sterowania z jednej funkcji do drugiej posu nam
funkcje setjmp i longjmp.
64
Wgb jzyka C
odtworzenie stanu programu jaki zosta zapamitany w zmiennej typu jmp_buf
przekazanej jako pierwszy argument. W wyniku takiego wywoania funkcji longjmp program znajduje si w punkcie powrotu z funkcji setjmp (bo w takim
momencie zosta zapamitany stan programu), przy czym warto zwracana przez
funkcj setjmp jest rwna drugiemu argumentowi funkcji longjmp (lub 1 jeeli
drugi argument by rwny 0). Na podstawie wartoci funkcji setjmp, program
jest w stanie odrni czy zostaa ona normalnie wywoana w wyniku zinterpretowania kolejnej instrukcji, czy te nastpi skok przy pomocy funkcji longjmp.
Dziaanie funkcji setjmp i longjmp jest czasami trudne do zrozumienia. Poniszy przykad powinien wyjani niejsnoci.
if(setjmp(buf))
{
/* cig instrukcji */
}
/* ... */
longjmp(buf,3);
3. Przeczanie zada
Zastanwmy si na pocztek, w jaki sposb dokona przeczenia procesora pomidzy
dwiema funkcjami. Jak wczeniej napisaem, uyjemy pary funkcji setjmp i longjmp
do wykonania dalekich skokw pomidzy procesami (funkcjami). Dla kadego procesu
bdziemy potrzebowa jednej zmiennej typu jmp_buf sucej do zapamitania stanu
programu w momencie przekazania sterowania do drugiego procesu. Obie zmienne musz by globalne, aby obie funkcje mogy si do nich odwoa.
jmp_buf buf1,buf2;
IV Programowanie wspbiene
65
longjmp(buf1,1);
/* w funkcji f1 */
if(setjmp(buf2)==0)longjmp(buf1,1);
/* w funkcji f2 */
Funkcja longjmp zostanie wywoana tylko wtedy, gdy setjmp zwrci warto zero. Nastpi to wic po wywoaniu setjmp w celu zapamitania kontekstu programu,
a nie nastpi po powrocie w to miejsce przy pomocy dalekiego skoku.
Sprbujmy teraz uoglni to rozwizanie na nieznan z gry ilo procesw.
Trzeba zdefiniowa jak struktur danych, ktra zapewniaaby istnienie jednego
bufora typu jmp_buf dla kadej funkcji, a take umoliwiaaby okrelenie jaka jest
nastpna funkcja w "acuszku".
struct el {
jmp_buf buf;
struct el *next;
};
Kad funkcja bdzie posiadaa wasny element typu struct el. W polu buf tego
elementu bdzie zapamitywany kontekst tej funkcji w chwili przeczania sterowania do kolejnego zadania. Pole next struktury bdzie wskazywao element typu
struct el skojarzony z funkcj, do ktrej ma by przekazane sterowanie. W ten sposb powstanie zaptlona lista o wzach typu struct el. Lista jest jednokierunkowa,
gdy kady jej element zawiera tylko pole wskazujce nastpny element. Do penej
manipulacji list jednokierunkow (w tym do usuwania elementw z listy) potrzebne s co najmniej dwie zmienne, wskazujce na dwa kolejne wzy listy:
struct el *cur,*last;
66
Wgb jzyka C
wza listy. Pole buf tego wza zostaje argumentem funkcji longjmp (zostanie
wykonany skok do nastpnej funkcji).
Pola buf w licie musz by zainicjowane przed pierwszym wywoaniem sekwencji przeczajcej zadania. eby to osign, umiecimy na pocztku kadej funkcji nastpujcy warunek:
if(setjmp(cur->buf)==0)return;
W tym miejscu przydaje si zadeklarowana wczeniej na wyrost zmienna last. Nastpnie trzeba przekaza sterowanie do kolejnego procesu:
longjmp(cur->buf,1);
4. Zapis praktyczny
Przedstawione powyej konstrukcje robi dobry uytek z funkcji setjmp
i longjmp umoliwiajc przeczanie funkcji - zada, ale w adnym wypadku nie
nadaj si do praktycznego zastosowania w programowaniu.
Stosujc definicje preprocesora mona zapisa te sekwencje w sposb duo
czytelniejszy. Zamy, e zapis ten musi spenia nastpujce warunki:
K zamiana napisanej i uruchomionej wczeniej funkcji na posta, w ktrej
if(setjmp(cur->buf)==0)return;
cur=last->next=cur->next;
\
longjmp(cur->buf,1);
if(setjmp(cur->buf)==0)
\
longjmp((last=cur,cur=(cur->next))->buf,1);
IV Programowanie wspbiene
67
\
static char is_a_process;
\
if((is_a_process=be_a_process)!=0)
\
#define END
if(setjmp(cur->buf)==0)return;
if(is_a_process!=0)
\
{
\
cur=last->next=cur->next;
\
longjmp(cur->buf,1);
\
}
\
} /* zamyka nawias otwarty w BEGIN */
#define _
if(is_a_process!=0)
if(setjmp(cur->buf)==0)
longjmp((last=cur,cur=(cur->next))->buf,1);
\
\
Zmienna be_a_process jest zmienn globaln. Jej warto wynosi cay czas zero
i jest ustawiana na jeden przez funkcj inicjujc procesy na czas inicjujcego
wywoania procesu. Funkcja inicjujca przydziela pami na struktur struct el dla
procesu i umieszcza j w licie.
void proces(FUNCTION f)
{
struct el *tmp;
_number++ ;
tmp=malloc(sizeof(struct el));
if(cur)
{tmp->next=cur->next; cur->next=tmp; }
else
{cur=tmp; cur->next=tmp;}
last=cur;
cur=tmp;
be_a_process=1;
(*f)();
be_a_process=0;
}
68
Wgb jzyka C
W funkcji proces wystpuje globalna zmienna _number. Jak wskazuje nazwa, jej
warto bdzie okrelaa liczb "ywych" procesw. Proces bdzie jej uywa do
sprawdzenia czy nie jest ju przypadkiem ostatnim ywym procesem.
Po zainicjowaniu wszystkich procesw mona rozpocz ich wspbiene wykonywanie. W tym celu zdefiniujemy makro RUN:
#define RUN
if(setjmp(_the_end)==0)
\
longjmp((cur=cur->next)->buf,1);
Globalna zmienna _the_end suy do zapamitania punktu, z ktrego zostao wywoane wspbiene wykonywanie procesw, i do ktrego naley wrci gdy
wszystkie procesy zakocz dziaanie.
Wyjanienia wymaga chyba jeszcze wyraenie w funkcji longjmp:
(cur=cur->next)->buf
\
static char is_a_process;
\
if((is_a_process=be_a_process)!=0)
\
if(setjmp(cur->buf)==0)return;
#define END
#define _
if(is_a_process!=0)
\
if(-- _number!=0)
\
{
\
cur=last->next=cur->next;
\
longjmp(cur->buf,1);
\
}
\
else
\
longjmp(_the_end,1);
\
} /* zamyka nawias otwarty w BEGIN */
if(is_a_process!=0 && _number>0 )
\
if(setjmp(cur->buf)==0)
\
longjmp((last=cur,cur=(cur->next))->buf,1);
Poniej znajduje si pena zawarto plikw proces.h i proces.c zawierajcych wszystkie opisane powyej definicje, a take definicje i deklaracje wszystkich uywanych zmiennych. Dodatkowo w pliku proces.h zostao
zdefiniowane makro ABORT, powodujce zakoczenie wszystkich procesw.
IV Programowanie wspbiene
/* plik proces.c
#include "proces.h"
69
Adam Sapek
*/
struct el *cur=NULL,*last=NULL;
zmiennych globalnych */
/* definicje
jmp_buf _the_end;
unsigned char _number=0,be_a_process=0;
void proces(FUNCTION f)
inicjujca proces */
{
struct el *tmp;
/* funkcja
_number++ ;
tmp=malloc(sizeof(struct el));
/*
przydziel pami */
if(cur!=NULL)
{tmp->next=cur->next; cur->next=tmp; }
/*
dopisz do kolejki */
else
{cur=tmp; cur->next=tmp;}
/* pierwszy proces ->
stwrz kolejk */
last=cur;
cur=tmp;
be_a_process=1;
(*f)();
/* inicjuj funkcj
jako proces */
be_a_process=0;
}
/* plik proces.h
Adam Sapek */
#ifndef __proces_h
#define __proces_h
#include <setjmp.h>
/* longjmp i setjmp */
#include <stdlib.h>
/* malloc */
#include <stdio.h>
/* NULL */
\
\
*/
*/
if(is_a_process!=0)
\
if(-- _number!=0)
\
{
\
cur=last->next=cur->next;
\
longjmp(cur->buf,1);
\
}
\
else
\
longjmp(_the_end,1);
\
} /* zamyka nawias otwarty w BEGIN */
\
\
70
Wgb jzyka C
longjmp((last=cur,cur=(cur->next))->buf,1);
/* makro RUN rozpoczyna wsplbiene wykonywanie procesw */
#define RUN
if(setjmp(_the_end)==0)longjmp((cur=cur->next)->buf,1);
/* makro ABORT powoduje natychmiastowe przerwanie WSZYSTKICH
procesw */
#define ABORT
if(is_a_process!=0)
{_number=0; longjmp(_the_end,1);}
buf;
*next;
5. Program wspbieny
Na pocztek wypada poda kilka oglnych zasad stosowania zdefiniowanych narzdzi. Piszc program wspbieny najlepiej napisa i uruchomi osobno kad
funkcj, ktra ma by w programie procesem. Przystosowanie napisanej wczeniej
funkcji do pracy wspbienej jest bardzo atwe i ogranicza do zmian "kosmetycznych". Po pierwsze na pocztku funkcji, po deklaracjach zmiennych, naley umieci makro BEGIN. Drugie makro, END, umieszcza si na kocu funkcji.
Teoretycznie ani makro BEGIN nie musi znajdowa si na samym pocztku funkcji, ani makro END na samym kocu, wymagane jest tylko, aby makro BEGIN
poprzedzao makro END. Umieszczajc wspomniane makra w innym miejscu naley jednak pamita, e tylko ta cz funkcji, ktra jest zawarta midzy nimi, bdzie wykonywana w pracy wspbienej. Kod poprzedzajcy makro BEGIN
wykona si tylko podczas inicjowania procesu funkcj proces, a kod nastpujcy
za makrem END nie wykona si w ogle.
Makro o nazwie _ (podkrelenie) umieszcza si w tych punktach, w ktrych proces ma by przerywany, a sterowanie ma by przekazywane do innych procesw. eby uzyska najlepsz wspbieno, najlepiej dopisa to makro do
kadego rednika koczcego instrukcj. Nie zawsze takie rozwizanie jest najlepsze. Sekwencja przekazania sterowania (makro _) ma swoje koszty, zarwno
czasowe jak i pamiciowe. Umieszczajc to makro inteligentnie w kadym procesie mona w duym stopniu sterowa dziaaniem programu. Mona w ten sposb na przykad uprzywilejowa niektre procesy lub ich fragmenty,
IV Programowanie wspbiene
71
uniemoliwi przerwanie krytycznych sekcji w procesach. Niektre zasady wyboru lokalizacji makra _ zostan opisane w dalszej czci tego rozdziau, przy
opisie przykadowego programu.
Najbardziej zdradzieckim bdem, jaki mona popeni w programie wspbienym, jest uycie w procesie zmiennej automatycznej. Pami na zmienne automatyczne jest przydzielana po wywoaniu funkcji i zwalniania po jej
zakoczeniu. Z tego powodu zmienne automatyczne rnych procesw mog
znale si w tym samym obszarze pamici (i zwykle tak si wanie dzieje).
Bdy powstae "dziki temu" mog by bardzo trudne do wykrycia nawet przy
pomocy debuggera. Dlatego naley bezwzgldnie pamita o zamienieniu
wszystkich zmiennych automatycznych w funkcjach/procesach na zmienne statyczne. Jest to waciwie jedyna zmiana merytoryczna jakiej naley dokona
w funkcji, eby moga sta si procesem wspbienym.
Programy wspbiene naley konsolidowa z moduem proces.obj powstaym po skompilowaniu pliku proces.c. Mona to zrobi na wiele sposobw,
np.:
K
Przykadowy program
Program demonstracyjny bdzie skada si z dwch procesw. Jeden z nich bdzie wczytywa kolejne bajty z podanego pliku i zlicza cakowit liczb bitw
rwnych jeden w pliku. Drugi proces na czas pracy pierwszego wygasi ekran
i bdzie wywietla na nim chodzcego "wa". Po przeczytaniu caego pliku
proces pierwszy przerwie dziaanie obydwu procesw i wrci do programu
gwnego, ktry wywietli liczb jedynek w pliku.
W wersji klasycznej funkcja zliczajca liczb jedynek w pliku mogaby wyglda na przykad tak:
void bits(void)
{
FILE *in;
unsigned c;
char name[80];
printf("\nNazwa pliku : ");
scanf("%s",name);
if(NULL!=(in=fopen(name,"rb")))
while(!feof(in))
{
c=getc(in);
72
Wgb jzyka C
while(c){ sum+=c&1; c/=2; }
}
}
IV Programowanie wspbiene
73
74
Wgb jzyka C
snake[a].x=snake[a-1].x;
snake[a].y=snake[a-1].y;
}
snake[0].x=x; snake[0].y=y;
a=*(char far *)0x46c;
while(*(char far *)0x46c-a<7);
}
END;
}
Waciwie umieszczenie tego makra obok kadego rednika nie szkodzi niczym
oprcz niepotrzebnego zwikszenia programu i zmniejszenia czytelnoci zapisu.
Kluczem do poprawnego umieszczenia sekwencji przeczajcej zadania jest spostrzeenie, e funkcja scr praktycznie cay czas spdza w ptli opniajcej (prosz sprbowa uruchomi t funkcj bez tej ptli !):
while(*(char far *)0x46c-a<7);
/* struktura
IV Programowanie wspbiene
75
/* w ma 4 czony */
void scr(void)
/* funkcja wygasza ekran i wywietla
chodzcego wa */
{
static unsigned direct=3,y,x,a;
static char dx[8]={0,1,2,1,0,-1,-2,-1},
dy[8]={-1,-1,0,1,1,1,0,-1};
BEGIN;
clrscr();
while(1)
{
do{ if(rand()%11==0)
/* rednio co 11 krokw
*/
direct=rand()%8;
/* losuj nowy
kierunek wa
*/
x=snake[0].x+dx[direct];
y=snake[0].y+dy[direct];
}while(x<1||x>=80||y<1||y>25);
/* czy mona w tym
kierunku ? */
wa
wa
gotoxy(x,y);
*/
gotoxy(snake[0].x,snake[0].y);
gotoxy(snake[1].x,snake[1].y);
gotoxy(snake[2].x,snake[2].y);
gotoxy(snake[3].x,snake[3].y);
for(a=3;a>=1;a--)
printf("");
/* rysuj
printf("");
printf("");
printf("");
printf(" ");
/* przesu
*/
{
snake[a].x=snake[a-1].x;
snake[a].y=snake[a-1].y;
}
snake[0].x=x; snake[0].y=y;
a=*(char far *)0x46c;
while(*(char far *)0x46c-a<7) _
taktw zegara */
}
END;
}
void main()
{
clrscr();
*/
proces(scr);
procesy
*/
proces(bits);
RUN;
procesy
*/
clrscr();
printf("W pliku znaleziono %lu jedynek",sum);
*/
}
/* odczekaj 7
/* wyczy ekran
/* inicjuj
/* uruchom
/* wypisz wynik
76
Wgb jzyka C
zapewnia ona synchronizacji: proces, ktry ma czyta jak dan ze zmiennej globalnej, nie wie, czy zostaa ona tam ju zapisana. Podobnie, proces zapisujcy dan
nie wie, czy poprzednia warto zostaa ju pobrana. Dopisanie odpowiednich
wskanikw synchronizujcych moe na tyle zaciemni zapis, e warto posuy
si preprocesorem w celu implementacji w miar oglnej metody komunikacji
midzy procesami. Dziki ukryciu szczegw w definicjach procesora
i zadeklarowaniu funkcji, zmiennych i struktur w osobnym pliku mona uzyska
bardzo atwe w uyciu i elegancko wygldajce narzdzia.
Do komunikacji midzy procesami uyjemy skrytki typu FIFO (ang. First In
First Out). Procesy "producenci" bd deponowa w skrytce dane okrelonego
typu, a procesy "konsumenci" bd je z niej pobiera. Jeeli skrytka bdzie pusta, konsumenci bd czeka na dane, jeeli za bdzie pena, producenci bd
czeka na wolne miejsce. Niezbdne jest, aby oczekiwanie ktrego procesu (czy
to konsumenta, czy producenta) nie powodowao zawieszenia pozostaych procesw. W przeciwnym przypadku czekajcy proces nigdy nie doczekaby si
zmiany stanu skrytki.
Typ danych przechowywanych w skrytce (w C++ moe to by nie tylko typ
standardowy, ale take klasa) oraz rozmiar skrytki bdzie mg by definiowany.
W przypadku niezdefiniowania typu i/lub rozmiaru bd przyjmowane wartoci
domylne: typ inti rozmiar rwny 100
Skrytka bdzie struktur zdefiniowan jak poniej:
struct {
unsigned long
typ
}_skrytka;
pocz,koniec;
wnetrze[rozmiar];
Pole pocz bdzie wskazywao nastpny element moliwy do pobrania, a pole koniec, pierwsze wolne miejsce w skrytce. Jeeli skrytka bdzie pusta, warto pola
pocz bdzie rwna wartoci pola koniec. Jeeli skrytka bdzie pena, zajdzie warunek:
_skrytka.koniec-_skrytka.pocz==rozmiar
Warunki sprawdzajce, czy skrytka jest pena czy pusta, mog przyda si
w programowaniu, dlatego zdefiniujemy je w postaci identyfikatorw preprocesora:
#define PELNA (_skrytka.pocz-_skrytka.koniec==rozmiar)
#define PUSTA (_skrytka.pocz==_skrytka.koniec)
Funkcje umieszczajca i usuwajca dan ze skrytki s bardzo proste i nie wymagaj chyba komentarza:
_umiesc(typ x)
{
if(!PELNA)
{
IV Programowanie wspbiene
77
_skrytka.wnetrze[(_skrytka.pocz++)%rozmiar]=x;
return 0;
}
return 1;
}
_usun(typ *x)
{
if(!PUSTA)
{
*x=_skrytka.wnetrze[(_skrytka.koniec++)%rozmiar];
return 0;
}
return 1;
}
S to funkcje wewntrzne dla moduu komunikacji midzy procesami; dla programisty przygotujemy wygodniejsze narzdzie. Jak wida, funkcje _umiesc
i _usun zwracaj warto 0 w przypadku sukcesu i warto 1 w przypadku niepowodzenia. Wykorzystujc t wasno zdefiniujemy makrodefinicje czekajce
w razie potrzeby na dane lub wolne miejsce w skrytce, ale nie zawieszajce przy
tym pozostaych procesw.
#define
#define
put(x)
get(x)
while(_umiesc(x))_
while(_usun(&x))_
78
Wgb jzyka C
#define __bufor_h
#ifndef __proces_h
#include "proces.h"
#endif
#ifndef rozmiar
#define rozmiar 100
skrytki */
#endif
#ifndef typ
#define typ int
skrytce */
#endif
/* domylny rozmiar
#define put(x)
skrytki */
while(_umiesc(x))_
#define get(x)
skrytki */
while(_usun(x))_
/* w dan do
/* pobierz dan ze
struct {
FIFO */
/* skrytka
/* dane
/* czy
/* czy
void _inicjuj(void)
/* inicjacja
skrytki */
{
_skrytka.pocz=_skrytka.koniec=0;
}
_umiesc(typ x)
/* funkcja umieszcza dan w
skrytce */
{
if(!PELNA)
{ _skrytka.wnetrze[(_skrytka.pocz++)%rozmiar]=x;
return 0;
}
return 1;
}
_usun(typ *x)
/* funkcja usuwa dan ze
skrytki */
{
if(!PUSTA)
{ *x=_skrytka.wnetrze[(_skrytka.koniec++)%rozmiar];
return 0;
}
return 1;
}
#endif
Zastosowanie skrytki jest bardzo proste. Program, ktry jej uywa powinien zawiera dyrektyw
#include "bufor.h"
IV Programowanie wspbiene
79
Do umieszczania danych w skrytce naley stosowa makro put, a do ich pobierania makro get. Naley pamita, e argumentem makra get jest wskanik do
zmiennej, w ktrej ma by umieszczona pobrana dana. Poniszy prosty programik
ilustruje zastosowanie skrytki.
/* plik prod.c */
#define typ char
#include "bufor.h"
void producent1(void)
umieszcza w skrytce */
{
static char c;
BEGIN
for(c='A';c<='Z';c++)
{ put(c); _ }
END
}
void producent2(void)
umieszcza w skrytce */
{
static char c;
BEGIN
for(c='z';c>='a';c--)
{ put(c); _ }
END
}
void konsument(void)
na ekranie */
{
static char c,x;
BEGIN
for(x=0;x<52;x++)
{
get(&c); _
putchar(c);
}
END
}
void main()
{
proces(producent1);
proces(producent2);
proces(konsument);
RUN;
*/
}
/* inicjuj procesy */
80
Wgb jzyka C
(kbhit()?getch():NIC)
(kbhit()?getche():NIC)
IV Programowanie wspbiene
81
static int _c;
\
_x=0;
\
do{
\
_c=getke();
\
if(_c!=NIC)
\
_buf[_x++]=_c;
\
_
\
}while(_c!='\r');
\
sscanf(_buf,form,arg);
\
}
Adam Sapek */
#ifndef __procesi_h
#define __procesi_h
#ifndef __proces_h
#include "proces.h"
#endif
#include <conio.h>
#define NIC
-1
#define getk()
klawiatury */
#define getke()
czytany znak */
(kbhit()?getch():NIC)
/* czytaj znak z
(kbhit()?getche():NIC)
/* j.w. + pisz
\
\
\
\
\
\
82
Wgb jzyka C
{
\
putch(' '); putch('\b'); \
if(_x>0)_x--;
\
}
\
else
\
if(_c!=NIC)
\
_buf[_x++]=_c;
\
_
\
}while(_c!='\r');
\
sscanf(_buf,form,arg);
\
}
#endif
Makrodefinicji pscanf uywa si dokadnie tak, jak funkcji z rodziny scanf. Jedyn rnic, o ktrej trzeba pamita, jest to, e ma ona ustalon liczb argumentw: cig format i jedna zmienna do wczytania.
Poniej przedstawiam programik ilustrujcy ile pracy mog wykona inne procesy w czasie, gdy jeden czyta z klawiatury.
/* plik mult.c */
#include "procesi.h"
unsigned long i;
void czytaj(void)
/* funkcja pyta o imi i wypisuje
pozdrowienie */
{
static char name[40];
BEGIN
printf("Jak sie nazywasz ?\n");
pscanf("%[^\r]",name);
/* wczytaj wiersz do
tablicy name */
printf("Ahoj %s !\n",name);
ABORT
/* przerwij wszystkie
procesy
*/
END
}
void licz(void)
/* funkcja mnoy w petli
dwie liczby */
{
static float x=3.14, y=1.73, z;
BEGIN
while(1){ z=y*x; i++; _ }
/* pomn x*y i zwieksz
licznik
*/
END
}
void main()
{
proces(licz);
/* inicjuj procesy
*/
proces(czytaj);
RUN
/* uruchom procesy
wspbienie */
printf("W miedzyczasie wykonalem %ld mnozen",i);
}