【问题标题】:Sharing a Native variable between Delphi Threads在 Delphi 线程之间共享本机变量
【发布时间】:2018-07-01 15:48:04
【问题描述】:

我假设如果线程之间的共享变量具有本机类型,那么原子性应该可以完成这项工作。

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

【问题讨论】:

  • 我猜这就是 System.SyncObjs 中的TInterlocked 的用途。

标签: multithreading delphi shared-variable


【解决方案1】:

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

改用原子增量函数。 TInterlocked 类方法,或AtomicIncrement

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

【讨论】:

  • 我已经检查了 AtomicIncrement 在 Delphi 中是如何实现的。它使用“lock xadd [eax],edx”。 TInterlocked.Increment(Counter) 和 TInterlocked.Decrement(Counter) 没有使用 Inc() 和 Dec() 函数,而是解决了这个问题。但是 NativeInt 不被接受为输入类型,我不得不将其更改为 Integer。谢谢。
  • 我怀疑您是否有任何理由将 NativeInt 用于执行算术运算的变量
  • @MehmetFide: NativeInt 基于平台架构映射到 IntegerInt64TInterlocked.Increment()TInterlocked.Decrement() 对这两种类型都有重载,但是它们的参数被声明为 var,因此您不能传递 NativeInt 变量,除非您进行类型转换。但是,是的,将NativeInt 用于非指针算术运算通常没有意义。
猜你喜欢
  • 1970-01-01
  • 2021-01-27
  • 1970-01-01
  • 2017-07-22
  • 2019-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多