multithreadingdelphishared-variable

Sharing a Native variable between Delphi Threads


I was assuming that if a shared variable between threads has native type, atomicity should do the job.

But as per output of the code below, it is not the case, at least for delphi.

Thread t1 is simply incrementing the counter 10M times. At the same time, thread t2 is decrementing the counter 10M times. So expected counter value at the end is 0 but I read different values each time.

What is the proper way of sharing a native variable between threads in Delphi without locking?

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;

Solution

  • Reading and writing of aligned variables is atomic. But the problem is that when you use inc and dec you are both reading and writing. By performing two memory accesses then the compound operation is no longer atomic.

    Use atomic increment functions instead. The TInterlocked class methods, or AtomicIncrement.

    As far as what is native about NativeInt, that refers to its size. It is an integral type the same size as a pointer. So 32 bits in a 32 bit process, 64 bits in a 64 bit process. These types are seldom used for pure Delphi code, usually for interop with third party libraries which might declare handle types using pointer sized integers.