Aby odpowiedzieć na pytanie, czy możliwa jest wieloprocesowość w Turbo Pascalu 6.0, należy najpierw zdefiniować to pojęcie. W komputerach klasy IBM PC wieloprocesowość polega na pozornym wykonywaniu dwóch (lub więcej) czynności w tym samym czasie. Obserwatorom wydaje się, że procesy wykonywane są jednocześnie - wynika to jednak tylko z dużej szybkości pracy komputera.
W rzeczywistości wiemy, że mikroprocesor może jednocześnie wykonywać tylko jeden rozkaż (wyjątkiem są chwile, kiedy mikroprocesor przekazuje wykonanie skomplikowanych działań matematycznych koprocesorowi, a sam wykonuje inne rozkazy). W systemach wielozadaniowych (takich jak Windows czy UNIX) program zarządzający pracą systemu stosuje podział czasu mikroprocesora między poszczególne zadania. Podział czasu odbywa się niejawnie.
Przy tak zdefiniowanym pojęciu wieloprocesowości na tytułowe pytanie należy odpowiedzieć : nie! Możemy jednak pokusić się o takie napisanie aplikacji w Turbo Vision, aby przynajmniej zewnętrznie sprawiała ona wrażenie wieloprocesowej.
Pseudowieloprocesowość pokażę na przykładzie obiektów, które oprócz wypełniania wnętrz swojego okna według z góry określonych zasad, nie będą wykonywały żadnych innych czynności. Zmiana wzoru wypełniającego wnętrze okna będzie procedurą wykonywaną "współbieżnie". We własnych programach, wykonujących inne czynności, należy pamiętać o konieczności podziału kodu procedur na odpowiednio małe fragmenty - tak, aby wszystkie mogły być wykonane, bez spowolnienia pracy programu. W programie swoim wykorzystałem procedurę Idle. Procedura ta jest wywoływana zawsze wtedy, kiedy wszystkie zdarzenia zostaną już obsłużone, a w kolejce do obsługi nie czeka żadne inne zdarzenie. Jeżeli procedury wykonywane przez nas krokowo mogą być wywołane niezależnieod naszej woli (tak, jak np. Draw w moim programie), należy przewidzieć mozliwości blokady wykonania "współbieżnego" w nieodpowiednim dla nas momencie fragmentu kodu.
W przedstawionym na wydruku programie należy zwrócić uwagę na następujące obiekty :
- TMWMultiApp - obiekt umożliwiający pracę pseudowieloprocesową. W obiekcie tym pole MultiTask jest tablicą, która umożliwia kolejne wywoływanie obiektów z wnętrza procedury Idle.
- TMWMultiWindow - obiekt pośredni. Po tym obiekcie muszą dziedziczyć własności wszystkie obiekty aktywowane okresowo. Pole Step służy do blokowania lub odblokowania wykonania "współbieżnych" kroków programu.
Na wydruku zaznaczono w komentarzach. które fragmenty programu są wykonywane jako pseudowspółbieżne. Mam nadzieję, że przykład ten zainspiruje Czytelników do tworzenia własnych, ciekawych rozwiązań w swoich programach.
{****************************************************************
**** Program : Demonstracja realizacji ****
**** pseudowieloprocesowości w Turbo Vision ****
**** Autor : Marek Wierzbicki, Łódź ****
****************************************************************}
{$X+, D-, L-}
Program MW_Multi_Tasking;
Uses App, Objects, Menus, Views, Drivers, Dos, GadGets;
Type PMWMultiWindow = ^TMWMultiWindow; { Obiekt pośredni }
TMWMultiWindow = Object(TWindow) { po którym dziedziczą }
Step : Boolean; { obiekty "współbieżne" }
Constructor Init(Var R: Trect; T: TTitleStr; wNr: Integer);
Procedure UpDate; Virtual;
End;
PMWOkno1 = ^TMWOkno1; { Obiekty "współbieżne" 1-4 }
TMWOkno1 = Object(TMWMultiWindow)
Clock : PClockView;
Constructor Init;
Procedure Draw; Virtual;
End;
PMWOkno2 = ^TMWOkno2;
TMWOkno2 = Object(TMWMultiWindow)
Linia : String[26];
Constructor Init;
Procedure Draw; Virtual;
End;
PMWOkno3 = ^TMWOkno3;
TMWOkno3 = Object(TMWMultiWindow)
Linia : String[10];
Constructor Init;
Procedure Draw; Virtual;
End;
PMWOkno4 = ^TMWOkno4;
TMWOkno4 = Object(TMWMultiWindow)
InsideFill : Char;
Constructor Init;
Procedure Draw; Virtual;
End;
MultiTaskArray = Array[0..3] Of PMWMultiWindow; { Tablica służąca do }
{ krokowego wywoływania funkcji "współbieżnych" }
PMWMultiApp = ^TMWMultiApp; { Aplikacja pseudowieloprocesorowa }
TMWMultiApp = Object(TApplication)
W1, W2 : Boolean;
W3, W4 : Boolean;
Ilosc : Byte;
MultiTask : MultiTaskArray;
LastSec : Word;
Constructor Init;
Procedure InitMenuBar; Virtual;
Procedure Idle; Virtual;
Procedure HandleEvent(Var E: TEvent); Virtual;
Destructor Done; Virtual;
End;
Const cmOkno1 = 1001;
cmOkno2 = 1002;
cmOkno3 = 1003;
cmOkno4 = 1004;
Constructor TMWMultiWindow.Init(Var R: TRect; T: TTitleStr; wNr: Integer);
Begin
TWindow.Init(R, T, wNr);
Step := False;
End;
Procedure TMWMultiWindow.Update; { Procedura wywołująca }
Begin { jeden krok "współbieżny" }
Step := True; { - odblokowanie "kroku współbieżnego" }
Draw; { wywołanie procedury zawierającej "kroki" }
Step := False; { - zablokowanie "kroku współbieżnego }
End;
Constructor TMWOkno1.Init;
Var R: TRect;
Begin
R.Assign(66,2,76,5);
TMWMultiWindow.Init(R,'',0);
Flags := 0;
R.Assign(1,1,9,2);
Clock := New(PClockView,Init(R));
Insert(Clock);
End;
Procedure TMWOkno1.Draw;
Begin
Clock^.UpDate; { fragment wykonywany pseudowspółbieżnie }
End;
Constructor TMWOkno2.Init;
Var R: TRect;
Begin
R.Assign(48,12,76,15);
TMWMultiWindow.Init(R,'',0);
Flags := 0;
Linia := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
End;
Procedure TMWOkno2.Draw;
Begin
If Step Then Linia := Copy(Linia,2,25)+Linia[1];
{ jeżeli można to wykonaj - tylko ten fragment }
{ procedury Draw który jest kodem wykonywanym }
{ "współbieżnie" }
WriteStr(1,1,Linia,1);
End;
Constructor TMWOkno3.Init;
Var R: TRect;
Begin
R.Assign(30,10,40,22);
TMWMultiWindow.Init(R,'',0);
Flags := 0;
Linia := '0123456789';
End;
Procedure TMWOkno3.Draw;
Var Linijka: String[8];
I: Byte;
Begin
If Step Then Linia := Copy(Linia,2,9)+Linia[1]; { fragment wykonywany "współbieżnie" }
Linijka[0] := #8;
For I := 0 To 9 Do
Begin
FillChar(Linijka[1],8,Linia[I+1]);
WriteStr(1,i+1,Linijka,1);
End;
End;
Constructor TMWOkno4.Init;
Var R: TRect;
Begin
R.Assign(30,2,60,9);
TMWMultiWindow.Init(R,'',0);
Flags := 0;
InsideFill := #178;
End;
Procedure TMWOkno4.Draw;
Var Linia: String[28];
i: Byte;
Begin
If Step Then { początek "kodu współbieżnego" }
Begin
If InsideFill = #178 Then
Begin
FillChar(Linia[1],28,#32);
InsideFill := #32;
End Else
Begin
FillChar(Linia[1],28,#178);
InsideFill := #178;
End;
Linia[0] := #28;
End;
For i := 1 To 5 Do WriteStr(1,1,Linia,1);
End;
Constructor TMWMultiApp.Init;
Var I: Byte;
Begin
TApplication.Init;
W1 := False;
W2 := False;
W3 := False;
W4 := False;
Ilosc := 0;
For I := 0 To 3 Do MultiTask[I] := Nil;
End;
Procedure TMWMultiApp.InitMenuBar;
Var R: TRect;
Begin
GetExtent(R);
R.B.Y := R.A.Y +1;
MenuBar := New(pMenuBar, Init(R, NewMenu(
NewSubMenu('~D~emo', hcNoContext, NewMenu(
NewItem('~Z~egar','',0, cmOkno1, hcNoContext,
NewItem('Przewijanie poziome ~H~','',0, cmOkno2,0,
NewItem('Przewijanie pionowe ~V~','',0, cmOkno3,0,
NewItem('~M~igotanie','',0, cmOkno4, hcNoContext,
NewLine(
NewItem('E~x~it','Alt+X',kbAltX, cmQuit, 0,
Nil))))))),
Nil))));
End;
Procedure TMWMultiApp.handleEvent(Var E: tEwent);
Var MWW : pMWMultiWindow;
Procedure Wstaw(Var W: Boolean);
Begin
Insert(MWW);
MultiTask[Ilosc] := MWW;
Inc(Ilosc);
ClearEvent(E);
W := True;
End;
Begin
tApplication.HandleEvent(E);
If (E.What = evCommand) Then
Case E.Command Of
cmOkno1: Begin
If Not W1 Then
Begin
MWW := New(PMWOkno1,Init);
Wstaw(W1);
End;
End;
cmOkno2: Begin
If Not W2 Then
Begin
MWW := New(PMWOkno2,Init);
Wstaw(W2);
End;
End;
cmOkno3: Begin
If Not W3 Then
Begin
MWW := New(PMWOkno3,Init);
Wstaw(W3);
End;
End;
cmOkno4: Begin
If Not W4 Then
Begin
MWW := New(PMWOkno4,Init);
Wstaw(W4);
End;
End;
End;
End;
Procedure TMWMultiApp.Idle;
Var I: Byte;
H,M,S,Sec: Word;
Begin
tApplication.Idle;
GetTime(H,M,Sec,S);
S := S Div 25;
If (LastSec <> S) And (Ilosc > 0) Then { cztery razy na sekundę wykonuj }
Begin { "kroki współbieżne" }
LastSec := S;
For I := 0 To Ilosc-1 Do MultiTask[I]^.Update;
End;
End;
Destructor TMWMultiApp.Done;
Var I: Byte;
Begin
tApplication.Done;
If Ilosc > 0 Then For I := 0 To Ilosc-1 Do Dispose(MultiTask[I],Done);
End;
Var MultiApp : TMWMultiApp;
Begin
MultiApp.Init;
MultiApp.Run;
MultiApp.Done;
End.
Marek Wierzbicki - PC Kurier 04/1992r.