0

I have a TParallel.&For loop. In this loop I create and use a TMyClass object to do the calculation. The result is stored in ResultList.

type
  TMyCommonClass=class(TList<Real>)
  private
    function DoCalculation(const AValue: Integer): Real;
  end;

type
  TMyClass=class(TList<Real>)
  private
    MyCommonClass: TMyCommonClass;
    function DoCalculation(const AValue: Integer): Real;
  end;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    MyCommonClass: TMyCommonClass;
  end;


function TMyCommonClass.DoCalculation(const AValue: Integer): Real;
var
  r: Real; //some local vars
begin
  r:=Items[AValue]*100; //some example calculations using local vars
  Result:=r;
end;

function TMyClass.DoCalculation(const AValue: Integer): Real;
begin
  Result:=MyCommonClass.DoCalculation(AValue);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
begin
  MyCommonClass:=TMyCommonClass.Create;
  for i := 0 to 1000000 do //add some example data to the list
  begin
    MyCommonClass.Add(i*0.01);
  end;
end;    

procedure TForm1.Button1Click(Sender: TObject);
var
  ALock: TCriticalSection;
  LoopResult: Tparallel.TLoopResult;
  ResultList: TList<Real>;
  i: Integer;
begin
  ResultList:=TList<Real>.Create;
  try
    ALock:=TCriticalSection.Create;
    try
      LoopResult:=TParallel.&For(0, 100000,
        procedure(AIndex: Integer)
        var
          MyClass: TMyClass;
          r: Real;
        begin
          MyClass:=TMyClass.Create;
          MyClass.MyCommonClass:=MyCommonClass;
          try
            r:=MyClass.DoCalculation(AIndex);
            begin
              ALock.Enter;
              try
                ResultList.Add(r);
              finally
                ALock.Leave;
              end;
            end;
          finally
            MyClass.Free;
          end;
        end);
    finally
      ALock.Free;
    end;

    ResultList.Sort;

    //show the result list
    for i := 0 to ResultList.Count-1 do
    begin
      Memo1.Lines.Add(FloatToStr(ResultList[i]));
    end;

  finally
    ResultList.Free;
  end;
end;

My example code works. But I am not sure if it is correct and will always work.

The method MyClass.DoCalculation calls the DoCalculation method of a TMyCommonClass object which is created at program start.

The TMyClass is created and destroyed for every loop. However the TMyCommonClass object only exist once so the different threads will access it in parallel.

I understand that I cannot write to TMyCommonClass without taking care of synchronisation but I am not sure about reading data and using methods.

The questions are:

  1. Is it OK to read data from the TMyCommonClass object?

  2. TMyCommonClass is a descendant of TList. Is it OK to read data using Items[i]?

  3. Is it OK to call a method of TMyCommonClass like I do in my example with TMyCommonClass.DoCalculation? What happens here to the local variables and the method parameters? Is it guaranteed that every thread gets it own memory space for the local variables and method parameters so that it is thread safe?

user3384674
  • 759
  • 8
  • 28

1 Answers1

2

Is it OK to read data from the TMyCommonClass object?

Reading is always thread-safe. The problem comes when you are writing to the common class. If you do any writes you need to make sure that other threads get either the data before the write or the data after the write.
The danger is that other threads get data halfway through the write (i.e. corrupted data).
You'll need to safe-guard against that using locking, a critical section, atomic write or some other mechanism.

TMyCommonClass is a descendant of TList. Is it OK to read data using Items[i]?

As I stated above reading is fine, however if you write to TCommon you'll need to take measures. TList has no thread safeguards built-in, you'll need to use a threadsafe list like TThreadList (which uses locking). If you have more advanced needs, see: How can I implement a thread-safe list wrapper in Delphi?

Is it OK to call a method of TMyCommonClass like I do in my example with TMyCommonClass.DoCalculation? What happens here to the local variables and the method parameters? Is it guaranteed that every thread gets it own memory space for the local variables and method parameters so that it is thread safe?

Yes, DoCalculation is fine, provided that you can always trust that {TMyCommonClass.}Items[AValue] returns valid data.
Local variables live on the stack and every thread gets its own stack that is guaranteed never to clash with other stacks.
Method parameters are passed through registers and the stack, so these are also safe.

Community
  • 1
  • 1
Johan
  • 74,508
  • 24
  • 191
  • 319