[ Pobierz całość w formacie PDF ]
.Nietrudno siê wiêc domyœliæ, i¿ wartoœælMaximumCount okreœla maksymaln¹ liczbê zasobów, które zostan¹ „przepuszczone”przez semafor, czyli — w momencie u¿ycia semafora jako parametru wywo³aniafunkcji synchronizuj¹cej (np.WaitForSingleObject()) zastan¹ go z dodatni¹wartoœci¹ licznika.Wyjaœnia to jednoczeœnie typowe zastosowanie semafora —s³u¿y on do synchronizacji dostêpu do zasobu, który mo¿e byæ wspó³dzielonyprzez co najwy¿ej zadan¹ a priori liczbê procesów; liczba ta jest jednoczeœniewartoœci¹ pocz¹tkow¹ licznika semafora.Powracaj¹c do naszego przyk³adu z inicjowaniem tablicy — mo¿e byæ ona„obs³ugiwana” w danej chwili przez co najwy¿ej jeden w¹tek — je¿eli wiêcu¿yjemy do synchronizacji semafora, jego wartoœæ pocz¹tkowa (lInitialCount)powinna wynosiæ w³aœnie 1.Poni¿szy wydruk, pochodz¹cy z projektu Sema4.dpr, przedstawia ostatni¹ ju¿wersjê dwuw¹tkowego inicjowania tablicy, oczywiœcie z wykorzystaniemsemafora.Wydruk 5.7.Synchronizacja dostêpu do tablicy za pomoc¹ semaforaunit Main;interfaceusesWindows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,StdCtrls;typeTMainForm = class(TForm)Button1: TButton;ListBox1: TListBox;procedure Button1Click(Sender: TObject);privateprocedure ThreadsDone(Sender: TObject);end;TFooThread = class(TThread)protectedprocedure Execute; override;end;varMainForm: TMainForm;implementation{$R *.DFM}constMaxSize = 128;varNextNumber: Integer = 0;DoneFlags: Integer = 0;GlobalArray: array[1.MaxSize] of Integer;hSem: THandle = 0;function GetNextNumber: Integer;beginResult := NextNumber; // zwróæ wartoœæ zmiennej globalnejInc(NextNumber); // zwiêksz zmienn¹ globaln¹end;procedure TFooThread.Execute;vari: Integer;WaitReturn: DWORD;beginOnTerminate := MainForm.ThreadsDone;WaitReturn := WaitForSingleObject(hSem, INFINITE);if WaitReturn = WAIT_OBJECT_0 thenbeginfor i := 1 to MaxSize dobeginGlobalArray[i] := GetNextNumber; // ustaw element tablicySleep(3 + Random(12)); // pozwól dzia³aæ innym w¹tkomend;end;ReleaseSemaphore(hSem, 1, nil);end;procedure TMainForm.ThreadsDone(Sender: TObject);vari: Integer;beginInc(DoneFlags);if DoneFlags = 2 then // upewnij siê, ¿e obydwa w¹tki zosta³y zwolnionebeginfor i := 1 to MaxSize do{ wype³nij listê zawartoœci¹ tablicy }Listbox1.Items.Add(IntToStr(GlobalArray[i]));CloseHandle(hSem);end;end;procedure TMainForm.Button1Click(Sender: TObject);beginhSem := CreateSemaphore(nil, 1, 1, nil);// utwórz i uruchom obydwa w¹tkiTFooThread.Create(False);TFooThread.Create(False);end;end.Utworzenie semafora nastêpuje bezpoœrednio przed utworzeniem obydwu w¹tków(wartoœci¹ pocz¹tkow¹ licznika semafora jest 1, co przed chwil¹ ju¿wyjaœniliœmy):hSem := CreateSemaphore(nil, 1, 1, nil);TFooThread.Create(False);TFooThread.Create(False);Przed rozpoczêciem krytycznej pêtli inicjuj¹cej elementy tablicy wywo³ywanajest funkcja synchronizuj¹ca WaitForSingleObject(), odwo³uj¹ca siê do semaforaza poœrednictwem jego uchwytu hSem:WaitReturn := WaitForSingleObject(hSem, INFINITE);Rozpoczêcie realizacji pêtli odbywa siê tylko wtedy, gdy oczekiwanie zakoñczy³osiê na skutek stanu sygnalnego semafora:if WaitReturn = WAIT_OBJECT_0then…W¹tek, opuszczaj¹c krytyczn¹ pêtlê (stanowi¹c¹ w tym wypadku rodzaj zasobu osynchronizowanym dostêpie) sygnalizuje ten fakt przez wywo³anie funkcjiReleaseSemaphore():Function ReleaseSemaphore(hSEmaphore: THandle; lReleaseCount: Longint;lpPreviousCount: Pointer): BOOL; stdcall;Pierwszym parametrem wywo³ania jest oczywiœcie uchwyt semafora u¿ywanego dosynchronizacji.Parametr lReleaseCount okreœla, o ile nale¿y zwiêkszyæ wartoœælicznika semafora — musi to byæ wartoœæ dodatnia, niekoniecznie równa 1 (choæakurat w tym przypadku jest).Mo¿liwoœæ zwiêkszenia licznika semafora o wiêcej ni¿ 1 w pojedynczym wywo³aniufunkcji ReleaseSemaphore() okazuje siê czasem niezwykle przydatna.Jakoprzyk³ad rozpatrzmy aplikacjê, w ramach której wiêksza liczba w¹tkówdrugorzêdnych wykorzystuje wspó³bie¿nie jakiœ zasób, zdolny „obs³u¿yæ”jednoczeœnie co najwy¿ej 10 z nich.Jedenasty w¹tek, zg³aszaj¹c ¿¹daniedostêpu do zasobu, napotka na zerow¹ wartoœæ semafora i bêdzie musia³poczekaæ.Za³Ã³¿my teraz, i¿ podczas tego oczekiwania wszystkie 10 w¹tkówzakoñczy³o siê w sposób awaryjny i ¿aden z nich nie zd¹¿y³ „podnieœæ”semafora, tj.wywo³aæ funkcji ReleaseSemaphore().Efekt jest taki, i¿aktualnie ¿aden w¹tek z zasobu nie korzysta, lecz dostêp do niego jest wdalszym ci¹gu zablokowany, gdy¿ licznik semafora ma wartoœæ 0.W tej sytuacjiw¹tek g³Ã³wny móg³by (po wykonaniu ewentualnych czynnoœci „naprawczych”)zadecydowaæ o ponownym udostêpnieniu zasobu — musia³by w tym celu zwiêkszyæodpowiednio wartoœæ licznika, w³aœnie o 10.Z powy¿szego opisu wynikajednoczeœnie „kosmopolityczny” charakter semaforów — ¿aden semafor nie jestprzynale¿ny do ¿adnego szczególnego w¹tku, a wiêc wszystkie w¹tki mog¹ u¿ywaægo bez ograniczeñ.Innym ciekawym wykorzystaniem procedury ReleaseSemaphore() — sugerowanym przezsystem pomocy Delphi — jest zablokowanie dostêpu do chronionego zasobu na czaswykonywania przez w¹tek lub aplikacjê pewnych czynnoœci inicjuj¹cych.Semaforjest tworzony z zerow¹ wartoœci¹ pocz¹tkow¹ licznika, co chwilowo blokujedostêp do zasobu; ostateczne nadanie licznikowi semafora ¿¹danej (dodatniej)wartoœci pocz¹tkowej nastêpuje w³aœnie za pomoc¹ funkcji ReleaseSemaphore().Próba zwiêkszenia licznika semafora ponad ustalony limit (okreœlony przeztrzeci parametr funkcji CreateSemaphore()) nie powiedzie siê — funkcjaReleaseSemaphore() zwróci wartoœæ FALSE.Ostatni parametr wywo³ania funkcji ReleaseSemaphore() — lpPreviousCount —umo¿liwia wskazanie zmiennej typu longint, do której wpisana zostaniepoprzednia wartoœæ licznika — tj.ta sprzed wywo³ania funkcji; podanie wartoœciNIL oznacza rezygnacjê z tej mo¿liwoœci
[ Pobierz całość w formacie PDF ]