How can I capture variables by anonymous method when using it in OTL?

后端 未结 2 813
广开言路
广开言路 2021-01-18 07:54

What I want to do:

I have a few objects in a genric list. I want to capture each of this object in anonymous method and execute this method as a separate OTL Task.

相关标签:
2条回答
  • 2021-01-18 08:29

    The documentation describes what's happening:

    Note that variable capture captures variables—not values. If a variable's value changes after being captured by constructing an anonymous method, the value of the variable the anonymous method captured changes too, because they are the same variable with the same storage.

    In your code, there is only one LObject variable, so all the anonymous methods you construct refer to it. As your loop makes progress, the value of LObject changes. The tasks haven't gotten a chance to start running yet, so when they do finally run, the loop has terminated and LObject has its final value. Formally, that final value is undefined after the loop.

    To capture the value of the loop variable, wrap creation of the task in a separate function:

    function CreateItemTask(Obj: TMyObject): TOmniTaskDelegate;
    begin
      Result := procedure(const Task: IOmniTask)
                begin
                  Writeln(Format('[Thread %d] Object ID: %d',[Task.UniqueID, Obj.ID]));
                end;
    end;
    

    Then change your loop code:

    CreateTask(CreateItemTask(LObject)).Unobserved.Run;
    
    0 讨论(0)
  • 2021-01-18 08:31

    Anonymous procedures captures variables rather than values. So you are capturing the variable LObject. Since this is a loop variable, the value of LObject changes. The anonymous procedures evaluate LObject when they execute rather than when the anonymous procedures are created.

    Rather than using an anonymous procedure, I'd probably just use a method of TMyObject. Try writing the code that way and I predict you will find it easier to understand.

    procedure TMyObject.TaskProc(const Task: IOmniTask);
    begin
      Writeln(Format('[Thread %d] Object ID: %d', [Task.UniqueID, Self.ID]));
    end;
    

    The reason for getting 4 lines of output rather than 3 is probably just that WriteLn is not threadsafe. Wrap the call to WriteLn in a lock to clear that up.

    0 讨论(0)
提交回复
热议问题