type
TInterlocked = class public class function CAS(var aTarget: integer; aCurrentVal, aNewVal: integer): boolean; overload; class function CAS(var aTarget: cardinal; aCurrentVal, aNewVal: cardinal): boolean; overload; class function CAS(var aTarget: pointer; aCurrentVal, aNewVal: pointer): boolean; overload; class function CAS(var aTarget: TObject; aCurrentVal, aNewVal: TObject): boolean; overload; class function CAS(var aTarget: LongBool; aCurrentVal, aNewVal: LongBool): boolean; overload; class function Inc(var aValue: integer): integer; overload; class function Inc(var aValue: int64): integer; overload; class function Dec(var aValue: integer): integer; overload; class function Dec(var aValue: int64): integer; overload; class function Add(var aValue: integer; aCounter: integer): integer; class function Sub(var aValue: integer; aCounter: integer): integer; class function SetValue(var aTarget: integer; aValue: integer): integer; end;
CAS is a acronym of "Compare And Swap", is a must operation in parallel code. Think on a certain class that has an owner thread as the first caller (for whatever reason), the easiest possibility is to use a critical section, but that affects the performance quite a lot, another possibility is to use CAS():
constructor TLock.Create;
begin
inherited;
FCurrentThread := 0; //Nobody owns me
FDepth := 0;
end;
destructor TLock.Destroy;
begin
Unlock;
inherited;
end;
function TLock.IsLocked: boolean;
begin
result := FCurrentThread <> 0; //Somebody owns me?
end;
function TLock.Lock(aTime: cardinal): boolean;
var
ticks: Cardinal;
begin
result := False;
ticks := GetTickCount;
repeat
if TryLock then
begin
result := True;
Break;
end;
Sleep(5);
until GetTickCount - ticks > aTime;
end;
procedure TLock.Lock;
begin
Lock(INFINITE);
end;
function TLock.TryLock: boolean;
begin
//The special part of the code
//It can be translated as
//
//ATOMIC ON
// if FCurrentThread = 0 then
// FCurrentThread := GetCurrentThreadId;
// Exit(FCurrentThread);
//ATOMIC OFF
//
//This can only happens once, so next thread will exit the function
//without success
//
//You could use a critical section here
//
result := (FCurrentThread = GetCurrentThreadId) or
(TInterlocked.CAS(FCurrentThread, 0, GetCurrentThreadId));
if result then
TInterlocked.Inc(FDepth); //How many times does my owner owns me?
end;
function TLock.Unlock: boolean;
begin
result := False;
if FCurrentThread = GetCurrentThreadId then //If caller = owner then release
begin
if TInterlocked.Dec(FDepth) = 0 then
begin
FCurrentThread := 0; //Now any other thread is able to own me
result := True;
end;
end;
end;
Next post I will talk about ParallelForEach<T>
No comments:
Post a Comment