マルチスレッド プログラミングでは、2 つ以上のスレッド/タスク間のデータ転送の同期を表すさまざまな用語を見つけることができます。
正確に言えば、あるアルゴリズムは次のとおりです。
1)Lock-Free
2)Wait-Free
3)Wait-Freedom
Lock-free の意味は理解できますが、同期アルゴリズムが Wait-Free または Wait-Freedom であると言えるのはいつですか? マルチスレッド同期用のコード (リング バッファ) をいくつか作成しましたが、Lock-Free メソッドを使用していますが、
- アルゴリズムは、このルーチンの最大実行時間を予測します。
- このルーチンを最初に呼び出すスレッドは、一意の参照を設定します (このルーチンの内部)。
- 同じルーチンを呼び出している他のスレッドがこの参照をチェックし、設定されている場合は、最初に関与したスレッドの CPU ティック カウント (測定時間) をカウントします。その時間が長い場合、関連するスレッドの現在の作業が中断され、彼のジョブがオーバーライドされます。
- 最後にタスクスケジューラから割り込まれた(リポーズされた)ためにジョブを終了していないスレッドは、自分に属していない場合は参照を確認し、再度ジョブを繰り返します。
したがって、このアルゴリズムは実際にはロックフリーではありませんが、使用中のメモリロックはなく、関連する他のスレッドは、再配置されたスレッドのジョブをオーバーライドする前に一定時間待機する (または待機しない) ことができます。
RingBuffer.InsertLeft関数を追加:
function TgjRingBuffer.InsertLeft(const link: pointer): integer;
var
AtStartReference: cardinal;
CPUTimeStamp : int64;
CurrentLeft : pointer;
CurrentReference: cardinal;
NewLeft : PReferencedPtr;
Reference : cardinal;
label
TryAgain;
begin
Reference := GetThreadId + 1; //Reference.bit0 := 1
with rbRingBuffer^ do begin
TryAgain:
//Set Left.Reference with respect to all other cores :)
CPUTimeStamp := GetCPUTimeStamp + LoopTicks;
AtStartReference := Left.Reference OR 1; //Reference.bit0 := 1
repeat
CurrentReference := Left.Reference;
until (CurrentReference AND 1 = 0)or (GetCPUTimeStamp - CPUTimeStamp > 0);
//No threads present in ring buffer or current thread timeout
if ((CurrentReference AND 1 <> 0) and (AtStartReference <> CurrentReference)) or
not CAS32(CurrentReference, Reference, Left.Reference) then
goto TryAgain;
//Calculate RingBuffer NewLeft address
CurrentLeft := Left.Link;
NewLeft := pointer(cardinal(CurrentLeft) - SizeOf(TReferencedPtr));
if cardinal(NewLeft) < cardinal(@Buffer) then
NewLeft := EndBuffer;
//Calcolate distance
result := integer(Right.Link) - Integer(NewLeft);
//Check buffer full
if result = 0 then //Clear Reference if task still own reference
if CAS32(Reference, 0, Left.Reference) then
Exit else
goto TryAgain;
//Set NewLeft.Reference
NewLeft^.Reference := Reference;
SFence;
//Try to set link and try to exchange NewLeft and clear Reference if task own reference
if (Reference <> Left.Reference) or
not CAS64(NewLeft^.Link, Reference, link, Reference, NewLeft^) or
not CAS64(CurrentLeft, Reference, NewLeft, 0, Left) then
goto TryAgain;
//Calcolate result
if result < 0 then
result := Length - integer(cardinal(not Result) div SizeOf(TReferencedPtr)) else
result := cardinal(result) div SizeOf(TReferencedPtr);
end; //with
end; { TgjRingBuffer.InsertLeft }
ここでRingBufferユニットを見つけることができます: RingBuffer、CAS 関数: FockFreePrimitives、およびテスト プログラム: RingBufferFlowTest