如果对象不提供内置的锁定机制,则可以使用临界区。临界区的工作方式类似于允许单个线程在任何时刻进入的门。要使用临界区,创建一个 System.SyncObjs.TCriticalSection
的全局实例。System.SyncObjs.TCriticalSection
有两个方法:Acquire
(用于阻止其他线程执行该区域)和 Release
(用于解除阻塞)。
每个临界区都与您想要保护的全局内存相关联。每个访问该全局内存的线程在访问之前都应首先使用 Acquire
方法,以确保没有其他线程正在使用它。当完成后,线程调用 Release
方法,以便其他线程可以通过调用 Acquire
访问全局内存。
警告:临界区仅在每个线程都使用它们来访问关联的全局内存时才有效。如果线程忽略临界区并在不调用 Acquire
的情况下访问全局内存,可能会引入同时访问的问题。
例如,考虑一个应用程序,它有一个全局临界区变量 LockXY
,用于阻止对全局变量 X
和 Y
的访问。任何使用 X
或 Y
的线程都必须在访问之前调用临界区方法,如下所示:
LockXY.Acquire; // 请求临界区
try
// 访问全局变量 X 和 Y
finally
LockXY.Release; // 释放临界区
end;
举例如下:
program SleepSortDemo;
{$APPTYPE CONSOLE}
uses
Windows, SysUtils, Classes, SyncObjs;
type
TSleepThread = class(TThread)
private
FValue: Integer;
FLock: TCriticalSection;
protected
constructor Create(AValue: Integer; ALock: TCriticalSection);
procedure Execute; override;
end;
constructor TSleepThread.Create(AValue: Integer; ALock: TCriticalSection);
begin
FValue := AValue;
FLock := ALock;
inherited Create(False);
end;
procedure TSleepThread.Execute;
begin
Sleep(1000 * FValue);
FLock.Acquire;
Write(FValue:3);
FLock.Release;
end;
const
ArrLen = 16;
var
A: array [0 .. ArrLen - 1] of Integer;
Handles: array [0 .. ArrLen - 1] of THandle;
Threads: array [0 .. ArrLen - 1] of TThread;
Lock: TCriticalSection;
I: Integer;
begin
// Generate random data
for I := 0 to ArrLen - 1 do
begin
A[I] := Random(ArrLen - 1);
Write(A[I]:3);
end;
Writeln;
// Create critical section and threads
Lock := TCriticalSection.Create;
for I := 0 to ArrLen - 1 do
begin
Threads[I] := TSleepThread.Create(A[I], Lock);
Handles[I] := Threads[I].Handle;
end;
// Wait until threads terminate
// This may take up to ArrLen - 1 seconds
WaitForMultipleObjects(ArrLen, @Handles, True, INFINITE);
// Destroy thread instances
for I := 0 to ArrLen - 1 do
Threads[I].Free;
Lock.Free;
Writeln;
Readln;
end.