在Delphi线程之间共享一个Native变量

时间:2018-01-22 22:29:46

标签: multithreading delphi shared-variable

我假设如果线程之间的共享变量具有本机类型,则原子性应该完成该任务。

但是根据下面代码的输出,情况并非如此,至少对于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;

1 个答案:

答案 0 :(得分:3)

读取和写入对齐变量是原子的。但问题是,当你使用incdec时,你都在阅读和写作。通过执行两次内存访问,复合操作不再是原子操作。

使用原子增量函数代替。 TInterlocked类方法,或AtomicIncrement

至于NativeInt的原生内容,指的是它的大小。它是一个与指针大小相同的整数类型。因此,32位进程中为32位,64位进程中为64位。这些类型很少用于纯Delphi代码,通常用于与第三方库互操作,第三方库可能使用指针大小的整数声明句柄类型。