信号量是建立在互斥量的基础之上,同时加入重要特性:提供了资源计数功能,因此预定义数量的线程同时可以进入同步的代码块中。
信号量是维护0到指定最大值之间的计数器的同步对象,当线程完成一次信号量的等待时,计数器自减1,当线程释放信号量对象时,计数器自增1。
借用上面的图书馆例子,信号量好像是多设几把管理钥匙。每次可以设定N把钥匙同时工作,那就有N个人员可以同时办理业务。
信号量使用的一般步骤:
1、声明一个全局的信号量名柄,如:hSem:THandle;
2、创建信号量:CreateSemphore(
lpSemaphoreAttributes:PSecurityAttributes;
lInitialCount,lMaximumCount:LongInt;
lpName:PChar):THandle;stdcall;
(lpSemaphoreAttributes参数,指向TSecurityAttributes记录的指针,一般可以缺省填入nil值;
lInitialCount参数,是信号量对象的初始计数,是0~lMaximumCount之间的数。当它大于0时,信号量就进入了信号状态,当WaiForSingleObject函数释放了一个线程,信号量计数就减1。使用ReleaseSemphore函数可以增加信号量计数;
lMaximumCount参数,是信号量对象计数的最大值;
lpName参数,指定信号量的名字。)
3、用等待函数WaiForSingleObject协调线程。
4、当一个线程用完一个信号,释放。使用ReleaseSemphore(
hSemaphore:THandle;
lReleaseCount:LongInt;
lpPreviousCount:Pointer):BOOL;StdCall;
(hSemphore参数,是信号量对象句柄;
lReleaseCount参数,要增加的信号量计数的数量;
lpPreviousCount参数,当前资源数量的原始值,一般为nil。)
5、最后关闭信号量句柄,CloseHandle(hSem)。
如果最大信号量计数为1,那么就相当于Mutex。
- unit Unit2;
- interface
- uses
- Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
- Dialogs, StdCtrls;
- type
- TForm2 = class(TForm)
- Button1: TButton;
- Label1: TLabel;
- Label2: TLabel;
- Label3: TLabel;
- Label4: TLabel;
- Label5: TLabel;
- procedure Button1Click(Sender: TObject);
- procedure FormCreate(Sender: TObject);
- procedure FormDestroy(Sender: TObject);
- private
- { Private declarations }
- public
- { Public declarations }
- end;
- var
- Form2: TForm2;
- hSem:THandle;
- implementation
- uses MyThread;
- {$R *.dfm}
- procedure TForm2.Button1Click(Sender: TObject);
- begin
- TMyThread.Create(Label1);
- TMyThread.Create(Label2);
- TMyThread.Create(Label3);
- TMyThread.Create(Label4);
- TMyThread.Create(Label5);
- end;
- procedure TForm2.FormCreate(Sender: TObject);
- begin
- hSem:=CreateSemaphore(nil,2,3,nil);
- end;
- procedure TForm2.FormDestroy(Sender: TObject);
- begin
- CloseHandle(hSem);
- end;
- end.{多线程类}
- unit MyThread;
- interface
- uses
- Classes,StdCtrls,SysUtils,Windows;
- type
- TMyThread = class(TThread)
- private
- { Private declarations }
- FLabel:TLabel;
- str:String;
- procedure Show;
- protected
- procedure Execute; override;
- public
- Constructor Create(aLabel:TLabel);
- end;
- implementation
- uses Unit2;
- { TMyThread }
- procedure TMyThread.Show;
- begin
- FLabel.Caption:=str;
- end;
- Constructor TMyThread.Create(aLabel: TLabel);
- begin
- FLabel:=aLabel;
- Inherited Create(False);
- end;
- procedure TMyThread.Execute;
- var
- i:Integer;
- begin
- { Place thread code here }
- FreeOnTerminate:=True;
- if WaitForSingleObject(hSem,INFINITE)=WAIT_OBJECT_0 then
- begin
- try
- for i := 0 to 2000 do
- begin
- if not Terminated then
- begin
- str:=Format('线程ID:%.4d,第%.4d个循环。',[GetCurrentThreadId,i]);
- Show;
- //Synchronize(Show);{不能使用,主线程访问线程类变量会卡死}
- end;
- end;
- finally
- ReleaseSemaphore(hSem,1,nil);
- end;
- end;
- end;
- end.
运行效果如下,
可以看到,每次只能有两个线程同时运行。