在 Delphi 个线程之间共享本机变量
Sharing a Native variable between Delphi Threads
我假设如果线程之间的共享变量具有本机类型,原子性应该可以完成这项工作。
但根据下面代码的输出,情况并非如此,至少 delphi。
线程 t1 只是将计数器递增 10M 次。
同时,线程 t2 将计数器递减 10M 次。
所以最后的预期计数器值为 0,但我每次都读取不同的值。
在 Delphi 中不加锁的线程之间共享本地变量的正确方法是什么?
procedure TForm1.Button1Click(Sender: TObject);
var
t1, t2: TThread;
Counter: NativeInt;
begin
Counter := 0;
// first thread to increment shared counter
t1 := TThread.CreateAnonymousThread(
procedure ()
var
i: Integer;
begin
for i := 1 to 10000000 do
Inc(Counter);
end
);
// second thread to decrement shared counter
t2 := TThread.CreateAnonymousThread(
procedure ()
var
i: Integer;
begin
for i := 1 to 10000000 do
Dec(Counter);
end
);
t1.FreeOnTerminate := false;
t2.FreeOnTerminate := false;
// start threads
t1.Start;
t2.Start;
// wait for them to finish
t1.WaitFor;
t2.WaitFor;
t1.Free;
t2.Free;
// print the counter, expected counter is 0
Caption := IntToStr(Counter);
end;
对齐变量的读写是原子的。但问题是,当你使用 inc
和 dec
时,你既在读又在写。通过执行两次内存访问,复合操作不再是原子的。
改用原子增量函数。 TInterlocked
class 方法,或 AtomicIncrement
.
就 NativeInt
的原生而言,这是指它的大小。它是一个与指针大小相同的整数类型。所以 32 位进程中的 32 位,64 位进程中的 64 位。这些类型很少用于纯 Delphi 代码,通常用于与第三方库的互操作,第三方库可能使用指针大小的整数声明句柄类型。
我假设如果线程之间的共享变量具有本机类型,原子性应该可以完成这项工作。
但根据下面代码的输出,情况并非如此,至少 delphi。
线程 t1 只是将计数器递增 10M 次。 同时,线程 t2 将计数器递减 10M 次。 所以最后的预期计数器值为 0,但我每次都读取不同的值。
在 Delphi 中不加锁的线程之间共享本地变量的正确方法是什么?
procedure TForm1.Button1Click(Sender: TObject);
var
t1, t2: TThread;
Counter: NativeInt;
begin
Counter := 0;
// first thread to increment shared counter
t1 := TThread.CreateAnonymousThread(
procedure ()
var
i: Integer;
begin
for i := 1 to 10000000 do
Inc(Counter);
end
);
// second thread to decrement shared counter
t2 := TThread.CreateAnonymousThread(
procedure ()
var
i: Integer;
begin
for i := 1 to 10000000 do
Dec(Counter);
end
);
t1.FreeOnTerminate := false;
t2.FreeOnTerminate := false;
// start threads
t1.Start;
t2.Start;
// wait for them to finish
t1.WaitFor;
t2.WaitFor;
t1.Free;
t2.Free;
// print the counter, expected counter is 0
Caption := IntToStr(Counter);
end;
对齐变量的读写是原子的。但问题是,当你使用 inc
和 dec
时,你既在读又在写。通过执行两次内存访问,复合操作不再是原子的。
改用原子增量函数。 TInterlocked
class 方法,或 AtomicIncrement
.
就 NativeInt
的原生而言,这是指它的大小。它是一个与指针大小相同的整数类型。所以 32 位进程中的 32 位,64 位进程中的 64 位。这些类型很少用于纯 Delphi 代码,通常用于与第三方库的互操作,第三方库可能使用指针大小的整数声明句柄类型。