我有一个Win32线程(没有TThread),它运行所有时间并迭代静态数组。主线程可以修改数组的字段。在没有 TThreadList 等组件的情况下使这个线程安全的最佳方法是什么(对于无 vcl 应用程序),只有 Windows 关键部分 (TRTLCriticalSection)?
法典:
type
T = record
Idx: Integer;
Str: string;
Num: Real;
Enabled: Boolean;
end;
var
A: Array[0..9] of T;
Cnt: Integer;
CS: TRTLCriticalSection;
procedure thread;
var
I: Integer;
begin
while True do
begin
for I := Low(A) to High(A) do
begin
if A[I].Enabled then
begin
//modify some fields from A[I]
Inc(A[I].Idx);
if A[I].Idx >= 10 then
begin
A[I].Enabled := False;
InterlockedDecrement(Cnt);
end;
end;
end;
if Cnt = 0 then Sleep(1);
end;
end;
procedure Add(...); //called only from mainthread
function GetFreeField: Integer;
begin
for Result := Low(A) to High(A) do
if not A[Result].Enabled then Exit;
Result := -1;
end;
var
I: Integer;
begin
I := GetFreeField;
if I = -1 then Exit;
//set fields A[I]
A[I].Enabled := True;
InterlockedIncrement(Cnt);
end;
开始时,数组初始化为 enable = false 和 cnt = 0。
以下修改是否足够?
procedure thread;
var
I: Integer;
begin
while True do
begin
for I := Low(A) to High(A) do
begin
EnterCriticalSection(CS);
if A[I].Enabled then
begin
LeaveCriticalSection(CS);
//modify some fields from A[I]
Inc(A[I].Idx);
if A[I].Idx >= 10 then
begin
EnterCriticalSection(CS);
A[I].Enabled := False;
LeaveCriticalSection(CS);
InterlockedDecrement(Cnt);
end;
end
else
LeaveCriticalSection(CS);
end;
if Cnt = 0 then Sleep(1);
end;
end;
procedure Add(...); //called only from mainthread
var
I: Integer;
begin
I := GetFreeField;
if I = -1 then Exit;
//set fields A[I]
EnterCriticalSection(CS);
A[I].Enabled := True;
LeaveCriticalSection(CS);
InterlockedIncrement(Cnt);
end;
在我看来,您的设计是这样的:
- 主线程只会将
Enabled
标志从False
切换到True
。 - 工作线程只会在相反的方向上切换标志。
- 除了我们在这里看到的代码之外,没有其他代码可以访问数组。
如果这是真的,那么没有关键部分的原始代码已经是线程安全的。至少它是在使用强内存模型的硬件上。例如英特尔 x86 或 x64 架构。Enabled
布尔值充当线程之间的同步屏障。
但是,您的整个设计在我看来是有缺陷的。while True
循环和Sleep
让我有些警觉。该线程将无缘无故地重复运行。当然,您应该仅在主线程对数组进行修改时才在线程中执行代码。我更喜欢使用信号(例如 Windows 事件)来唤醒线程。