简体   繁体   中英

Why the value of the loop variable is not stored in a dynamically created AnonymousThread

I have some academic interest of how I can store a unique identifier in a dynamically created TThread .

I create something like this:

procedure TForm1.Button1Click(Sender: TObject);
var thrn:word;
begin
for thrn := 0 to 5 do//<--- this is a loop variable that should give the unique numbers
  TThread.CreateAnonymousThread(
    procedure()
    var
      i: longint;
      r: double;
      thrns:string;
    begin
      thrns:=inttostr(thrn);//in this thread? variable I try to store the ID as string
      repeat
        for i := 0 to 100000000 do
        begin
          r := random(high(i));//this loop gives some dummy job 
          r := sqr(r);         //to the thread to slow it down
        end;
        TThread.Synchronize(nil,
          procedure()
          begin
            memo1.Text:=memo1.Text+#13#10+
              'done'+thrns;//it returns strange IDs including '6'
          end);
      until false;
    end).Start;
end;

Can I pass a unique identifier to the dynamically created thread so that it could show it in its synchronize method?

This is a classic misunderstanding. We understand that anonymous methods capture, but what do they capture? The value or the variable?

The answer is the latter. They capture the variable. There is a single variable, thrn , that each of your six anonymous methods capture. Since there is one variable, there is only one value, at any one moment in time.

Of course, since you are executing code in threads, you have a data race on this variable. Hence my "at any one moment in time" proviso. And that's why you have unrepeatable, unpredictable results. And you are likely to access the loop variable after the loop has completed and the value then is undefined.

If you wish to have a different value for each anonymous method, you must make a new variable for each anonymous method. My answer to another question demonstrates that: Anonymous methods - variable capture versus value capture .

So, to illustrate in your context we need some more scaffolding.

function GetThreadProc(thrn: Integer): TProc;
begin
  Result := 
    procedure
    begin
      // thrn is passed by value, so a copy is made, i.e. a new variable
      ....
    end;
end;

....

procedure TForm1.Button1Click(Sender: TObject);
var 
  thrn: Integer;
begin
  for thrn := 0 to 5 do
    TThread.CreateAnonymousThread(
      GetThreadProc(thrn)).Start;
end;

You have to capture the value of your identifier. Here is an example how to do that.

procedure TForm1.Button1Click(Sender: TObject);
  function GetAnonProc( ID: Word): TProc;
  begin
    Result :=
      procedure
      var
        i: longint;
        r: double;
        thrns:string;
      begin
        thrns:= inttostr(ID);// Capture value
        repeat
          for i := 0 to 100000000 do
          begin
            r := random(high(i));//this loop gives some dummy job
            r := sqr(r);         //to the thread to slow it down
          end;
          TThread.Synchronize(nil,
            procedure()
            begin
              memo1.Text:=memo1.Text+#13#10+
                'done'+thrns;//it returns strange IDs including '6'
            end);
        until false;
      end;

  end;
var
  thrn:word;
  p: TProc;
begin
  for thrn := 0 to 5 do
  begin
    p := GetAnonProc(thrn); // Capture thrn value
    TThread.CreateAnonymousThread(p).Start;
  end;
end;

The code above captures 6 different references to a local ID variable. Each with a different value.

The code in the question captures a single variable reference. Since you cannot control when the threads are running, there is no way to predict what value they will retrieve from the variable reference. The value 6 you observe is because of the fact that a loop variable's value is undefined after the loop is completed.

To further understand how anonymous methods works and use variable binding, read Variable Binding Mechanism .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM