Mega Code Archive

 
Categories / Delphi / Examples
 

Semaphore and mutex

Title: Semaphore and mutex Question: Understanding semaphores and mutex Answer: In windows there are 4 synchronization objects to use with multithreading applications : 1- Critical Section 2- Event 3- Semaphore 4- Mutex but in Delphi there is no encapsulation for Mutex or Semaphore there is only TCriticalSection and TEvent and they are implemented in SyncObjs Unit Semaphores : Semaphores Objects are used to manipulate a group of threads. To make specified count of threads work together. For example .. you have a file on your hard disk that share some info using a network application and you dont want more than 4 connections to access this file simultaneously for performance reasons but there is 10 connections currently connected to you application and all of them need to access this file . So you have to Synchronize between 10 connections by letting 4 connections only access this file simultaneously .. in this case you have to use Semaphore You need this list of function to handle semaphore object CreateSemaphore //used to make new object and specify //number of threads that can gain access //in the same time OpenSemaphore //access already exist object by name CloseHandle //release handle of opened handle to the object ReleaseSemaphore //decrement count of threads that use semaphore WaitForSingleObject //make thread wait to gain access from semaphore //and increment count of threads by one I have made this example to demonstrate how semaphores work This example show you how to move specified count of balls together uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls,Contnrs; type TMainForm = class(TForm) btnSimultaneousRunningThreads: TButton; procedure btnSimultaneousRunningThreadsClick(Sender: TObject); private //this array hold balls positions //each slot in this array is used for one ball //and each value should be between 0 and 100 FBalls:array[1..10] of byte; FThreadsList:TObjectList; //Handle for Semaphore Object FSemaphore:Cardinal; protected procedure DoCreate; override; procedure DoClose(var Action: TCloseAction); override; procedure Paint; override; { Private declarations } public destructor Destroy; override; procedure LaunchThreads; procedure DestroyThreads; end; TModifyThread=class(TThread) protected procedure DoTerminate; override; protected FForm:TMainForm; FSlot:Byte;//Slot of FBalls array that //used to define visual ball position procedure Execute; override; end; var MainForm: TMainForm; implementation procedure TMainForm.DoClose(var Action: TCloseAction); begin inherited; DestroyThreads; CloseHandle(FSemaphore); end; procedure TMainForm.Paint; var x:Integer; i:Integer; BallPos:Integer; R:TRect; begin inherited; Canvas.Brush.Color:=clWhite; Canvas.FillRect(ClientRect); x:=ClientWidth div 11; //this loop is used to draw 10 lines with 10 balls on them //on each line there is ball that is differ in it's position //according to the corresponding slot in FBalls array for i:=1 to 10 do begin Canvas.Pen.Color:=clBlue; Canvas.Pen.Width:=2; Canvas.PenPos:=Point(x * i,20); Canvas.LineTo(x * i,ClientHeight-20); BallPos:=FBalls[i]; R.Top:=(ClientHeight-20) - ( (ClientHeight-40)*(BallPos) div 100 ) -5; R.Left:=(x*i) -5; R.Right:=R.Left +10; R.Bottom:=R.Top+10; Canvas.Brush.Color:=clRed; Canvas.Pen.Width:=1; Canvas.Pen.Color:=clRed; Canvas.Ellipse(R); end; end; { TModifyThread } procedure TModifyThread.DoTerminate; begin inherited; FForm.FThreadsList.Extract(Self); end; procedure TModifyThread.Execute; var x:Integer; Semaphore:Cardinal; begin inherited; //Access already created semaphore object by name Semaphore:=OpenSemaphore(EVENT_ALL_ACCESS,false,'My Semaphore'); while true do begin //this function will hold the execution of the current thread //unitl the state of Semphore object turn to Signaled state WaitForSingleObject(Semaphore,INFINITE); Sleep(250); //Change current position of the ball x:=FForm.FBalls[FSlot]+10; if x100 then x:=0; FForm.FBalls[FSlot]:=x; FForm.Invalidate; Sleep(250); //Mean current thread finished with semaphore object //to let another threads get access ReleaseSemaphore(Semaphore,1,nil); if Terminated then begin CloseHandle(Semaphore); exit; end; end; CloseHandle(Semaphore); end; procedure TMainForm.btnSimultaneousRunningThreadsClick(Sender: TObject); var Num:Integer; val:String; begin DestroyThreads; CloseHandle(FSemaphore); //Be sure to close all opened handles of our semaphore //in order to destroy it if InputQuery('','Enter number simultaneous runing threads'+sLineBreak+ 'this value should be between 1 and 10',val) then begin Num:=StrToIntDef(Val,0); if Num10 then Num:=10 else if Num Num:=1; //Recreate Simaphore Object with new options FSemaphore:=CreateSemaphore(nil,Num,Num,'My Semaphore'); end; LaunchThreads; end; destructor TMainForm.Destroy; begin FThreadsList.Free; inherited; end; procedure TMainForm.DestroyThreads; //this function used to terminate all instances of threads //and wait until we be sure that no thread is still running var i:Integer; begin for i:=0 to FThreadsList.Count-1 do TThread(FThreadsList[i]).Terminate; while FThreadsList.Count0 do Application.ProcessMessages; end; procedure TMainForm.LaunchThreads; function CreateThread(Slot: Byte): TModifyThread; begin result:=TModifyThread.Create(true); result.FForm:=Self; result.FSlot:=Slot; result.Resume; FThreadsList.Add(result); end; var i:byte; begin DestroyThreads; for i:=1 to 10 do CreateThread(i); end; procedure TMainForm.DoCreate; begin inherited; //This list is used to hold created instances of threads //in order to manipulate them FThreadsList:=TObjectList.Create(false); DoubleBuffered:=true; //in order to prevent flickering //Create Semphore Object with specified name FSemaphore:=CreateSemaphore(nil,10,10,'My Semaphore'); //Create 10 instances of TModifyThread LaunchThreads; end; Mutex : Mutex object is not used only to synchronize between threads with in single process, you can also use it to synchronize between multiple threades in more than one process and to synchronize between processes it selves. For example you can ensure that there is only one instance of your application by creating a mutex with specified name when you launch you application. But before you create your mutex you should insure that there is no other mutex created before with the same name or that will mean there is another instance already running now . And you have to destroy your mutex when you terminate your application to let another instances work . CreateMutex //create new mutex object with specified name OpenMutex //get handle to already running mutex by its name WaitForSingleObject //wait for ownership to the mutex ReleaseMutex //release ownership of mutex and let another threads //be able to take ownership CloseHandle //close opened Mutex and destroy it when no more //handles opened For example to prevent more than one instance of you application work Just modify you application DPR file to look like this program MyProgram; uses Forms, Dialogs, MainForm in 'MainForm.pas', Windows; {$R *.res} var Mutex:Cardinal; Begin //look for previous created mutex with our application name Mutex:=OpenMutex(MUTEX_MODIFY_STATE,false,'My Application Name'); if Mutex0 then begin CloseHandle(Mutex); ShowMessage('There is another instance of this application already running'); exit; end; //there is no previous Mutex so create new one Mutex:=CreateMutex(nil,false,'My Application Name'); //take ownership of our mutex WaitForSingleObject(Mutex,INFINITE); //run program main loop Application.Initialize; Application.CreateForm(TMainForm, MainForm); Application.Run; //destroy our mutex CloseHandle(Mutex); end.