3

my code is running a for loop to process some data like here

procedure printValue(Value: Integer);
begin
  TThread.Synchronize(TThread.Current, procedure
  begin
   form1.memo1.lines.add( Value.ToString );
  end);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
  myThread : TThread;
  Proc1: TMyProc;
begin
  for I := 0 to 10 do
  begin
    myThread := TThread.CreateAnonymousThread(
    procedure
    begin
      printValue( i );
    end);
    myThread.Start;
  end;
end;

this code out put is like this:

3
5
6
8
9
11
10
4
11
4
7

this is not good so i add a small delay like sleep(1) after thread start.this will fix output problem but not a good idea because in a large loop block the ui thread so try to use this document as help so my code changed like this:

function CaptureValue(Value: Integer): TMyProc;
begin
  Result := procedure begin Writeln(Value); end;
end;

procedure printValue(Value: Integer);
begin
  TThread.Synchronize(TThread.Current, procedure
  begin
   form1.memo1.lines.add( Value.ToString );
  end);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
  myThread : TThread;
  Proc1: TMyProc;
begin
  for I := 0 to 10 do
  begin

    myThread := TThread.CreateAnonymousThread(
    procedure
    begin
      Proc1:= CaptureValue(i);
      printValue( Proc1 );
    end);
    myThread.Start;
  end;
end;

but i got [dcc32 Error] Unit11.pas(57): E2010 Incompatible types: 'Integer' and 'procedure, untyped pointer or untyped parameter' error.

what is wrong with my code?

3
  • 1
    What is wrong is explained in the error message, your PrintValue expects an integer but you're passing a reference to a procedure. Anyway, I don't think your problem is related with variable capture. You simply can't tell the threads when to start/finish. Commented Jun 9, 2019 at 16:42
  • ... even the sleep is not guaranteed to work, although it will increase the likelihood of getting your expected result. Commented Jun 9, 2019 at 16:52
  • @SertacAkyuz yes for this reason i have to avoid sleep. Commented Jun 9, 2019 at 17:14

1 Answer 1

6

Anonymous procedures capture variables, not values. This is documented behavior. So in both of your examples, your threads are sharing a single I variable with no synchronization over the access of that variable.

You had the right idea to pass I to a procedure and then capture it from the argument list before using it. However, you went about it the wrong way.

The reason your second example actually fails to compile is because you are misusing CaptureValue(). It returns a TMyProc, which is clearly a reference to procedure (which BTW, the RTL already has a TProc for that same purpose). You are passing an anonymous procedure as-is to printValue(), which takes an Integer instead. That is what the compiler error is complaining about.

The way you are using the return value, CaptureValue() would have to instead return an anonymous function that itself returns an Integer (ie, have CaptureValue() return a reference to function: Integer, aka TFunc<Integer>), then call that function and pass that return value to PrintValue().

You are still capturing I itself before passing it to CaptureValue(), though, so you still have threads sharing I. You would need to call CaptureValue() from inside the loop directly before calling CreateAnonymousThread(). But then, your threads would be capturing and sharing the Proc1 variable instead, so you would be right back to the original problem.

With that said, try something more like this instead:

procedure PrintValue(Value: Integer);
begin
  TThread.Synchronize(nil,
    procedure
    begin
      Form1.Memo1.Lines.Add( Value.ToString );
    end
  );
end;

function CaptureAndPrintValue(Value: Integer): TProc;
begin
  Result := procedure
    begin
      printValue( Value );
    end
  );
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to 10 do
  begin
    TThread.CreateAnonymousThread(
      CaptureAndPrintValue(I)
    ).Start;
  end;
end;

Or, you can let the RTL handle the threading for you:

uses
  ..., System.Threading;

procedure PrintValue(Value: Integer);
begin
  // can't use TThread.Synchronize() with TParallel, as it
  // doesn't service the main message queue while looping...
  TThread.Queue(nil,
    procedure
    begin
      Form1.Memo1.Lines.Add( Value.ToString );
    end
  );
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  TParallel.For(0, 10,
    procedure(I: Integer)
    begin
      PrintValue(I);
    end
  );
end;
Sign up to request clarification or add additional context in comments.

5 Comments

thank you but the first code output is : 0 2 3 6 7 10 1 8 4 5 9 the second code is hanged and dont show any output!
@peimanF. That output is perfectly fine given the threaded nature of your loop. The order of the output numbers is not guaranteed. If you are expecting the numbers to be output in order, then you don't understand how threads work. But you can see that no numbers are being repeated anymore, unlike with your original code. As for the hang, I forgot that TThread.Synchronize() doesn't work with TParallel.For().i updated that example to use TThread.Queue() instead
yes, if data doesn't lose is enough for me at this point.but why when a thread start sooner pass value later to memo !?!?
@peimanF. That is just how threads work. They run in parallel. Just because you start them in order does not mean they will end up syncing back to the UI in the same order. It depends on system timing. The timing between when a thread is created and when it actually starts running. The timing of task switches between threads running on the same CPU. The timing between threads running on different CPUs.
The timing of task switches between threads running on the same CPU. The timing between threads running on different CPUs really understandable.thank you for your useful information..

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.