delphitthread

Delphi - Can a TThread change the value of variable in main VCL thread?


Using: Delphi XE2, Windows VCL Forms application

Can a TThread during its execution change the value of a variable in the main VCL thread?

The need is to update an Integer which is declared as a field of the TForm class. It will be passed to the TThread as a var variable in an overloaded (and reintroduce) Create constructor method.

Are there any fallbacks in this?


Solution

  • Yes, threads can modify variables. Variables don't belong to threads. Variables can belong to form or thread objects, but a thread object (i.e., an instance of TThread or its descendants) is distinct from the OS execution thread.

    Objects can have code that runs in multiple threads. Your TThread.Create method runs in the context of the thread that calls it, which is often your main thread. The Execute method, on the other hand, runs in the context of the created OS thread. But obviously, both methods can access the fields of the TThread object, so that answers the question of whether two OS threads can access the same variable.

    You'll have trouble accessing the form variable in the way you describe, though. Passing it to the constructor as a var parameter will allow the constructor to modify it, but as I mentioned above, the constructor doesn't run in the context of the new OS thread. To allow the new thread to access that variable, you'd need to store a pointer to it instead of passing it by reference. For example:

    type
      TSteveThread = class(TThread)
      private
        FVariable: PInteger;
      protected
        procedure Execute; override;
      public
        constructor Create(Variable: PInteger);
      end;
    
    constructor TSteveThread.Create;
    begin
      inherited Create(False);
      FVariable := Variable;
    end;
    
    procedure TSteveThread.Execute;
    begin
      // Access FVariable^ here.
    end;
    

    Create it like this:

    procedure TSteveForm.ButtonClick;
    begin
      TSteveThread.Create(@Self.Variable);
    end;
    

    An alternative is to pass a reference to the form instead, and then access the form's field through that reference. For example:

    type
      TSteveThread = class(TThread)
      private
        FForm: TSteveForm;
      protected
        procedure Execute; override;
      public
        constructor Create(Form: TSteveForm);
      end;
    
    constructor TSteveThread.Create;
    begin
      inherited Create(False);
      FForm := Form;
    end;
    
    procedure TSteveThread.Execute;
    begin
      // Access FForm.Variable here.
    end;
    

    Create it like this:

    procedure TSteveForm.ButtonClick;
    begin
      TSteveThread.Create(Self);
    end;
    

    In either case, you need to take the usual precautions about controlling simultaneous access to the data by multiple threads. The bottom line is that both threads can access the data.