Delphi: Thread Variables

A somewhat less known feature of Delphi is thread variables. Thread variables are variables that can contain a different value for each thread in the application. They are defined like regular variables, the only difference is that the “var” keyword is replaced with “threadvar”. In the example below I’ll show you how they work in a small console application. Note that I used the TThread class for convenience, however in reality you’ll only ever need this for procedural code as you can add fields to your TThread subclass to store data in for each thread. The example uses the main thread and a separated thread to show you how it works.

program ThreadVarTest;

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes;

type
  TTestThread = class(TThread)
  protected
    procedure Execute; override;
  public
    constructor Create;
  end;

threadvar
  TestVar: string;

var
  i: Integer;

procedure Test;
begin
  WriteLn('Original TestVar: ' + TestVar);
  TestVar := 'Asdf' + IntToStr(i);
  Inc(i);
  WriteLn('New TestVar: ' + TestVar);
end;

{ TTestThread }

constructor TTestThread.Create;
begin
  inherited Create(False);
  FreeOnTerminate := True;
end;

procedure TTestThread.Execute;
begin
  Test;
end;

begin
  i := 0;
  Test;
  WriteLn('Starting thread.');
  TTestThread.Create;
  ReadLn;
  WriteLn(TestVar);
  ReadLn; // Prevent window from closing
end.

As you can see when running it, even though the content of the thread variable was changed, the variable was still empty when accessed by the new thread. I’ve added the incremental number to show you that when after you press a key when the thread has exited and you check the thread variable content for the main thread, it will show Adsf0 as it was stored by the main thread originaly.

One thought on “Delphi: Thread Variables

  1. Enrico Biscotti say:

    So 9 nine years after this post, it is still useful. I was having trouble understanding why my thread local variables were not working right in my C++ 10.1 program. I was able to take what Frédéric had posted, rewrite it in C++ and convince myself that the thread variables were working exactly as expected. With that piece of knowledge, I was able to go back into my program and figure out what I was doing wrong.

    If it is useful to anyone, here is my program. Not pretty but I learned a lot from it:

    #include
    #include
    #pragma hdrstop
    #include
    #include

    //—————————————————————————

    // the thread local variable seems to work with this declaration
    __declspec(thread) char TestVar[256];
    /* Output on console window:
    main() calls Test().
    Original TestVar at call to Test() (should be blank):
    Address of TestVar : 005621F0
    New TestVar at exit of Test(): Asdf0
    Starting new TTestThread which calls Test().
    Starting new TTestThread which calls Test().
    Original TestVar at call to Test() (should be blank):
    Address of TestVar : 0058F128
    New TestVar at exit of Test(): Asdf1
    Original TestVar at call to Test() (should be blank):
    Address of TestVar : 0058F500
    New TestVar at exit of Test(): Asdf2
    TestVar at main() exit (should be Asdf0) Asdf0
    */
    //—————————————————————————
    // if I change the declaration as follows, program does not work correctly
    //char TestVar[256];
    /* Output on console window:
    main() calls Test().
    Original TestVar at call to Test() (should be blank):
    Address of TestVar : 00421A60
    New TestVar at exit of Test(): Asdf0
    Starting new TTestThread which calls Test().
    Starting new TTestThread which calls Test().
    Original TestVar at call to Test() (should be blank): Asdf0
    Address of TestVar : 00421A60
    New TestVar at exit of Test(): Asdf1
    Original TestVar at call to Test() (should be blank): Asdf1
    Address of TestVar : 00421A60
    New TestVar at exit of Test(): Asdf2
    TestVar at main() exit (should be Asdf0) Asdf2
    */
    //—————————————————————————

    int i;
    //—————————————————————————

    void Test(void) {
    printf(“Original TestVar at call to Test() (should be blank): %s\n”, TestVar);
    printf(“Address of TestVar : %08X\n”, &TestVar);
    sprintf(TestVar, “Asdf%d”, i);
    i++;
    printf(“New TestVar at exit of Test(): %s\n”, TestVar);
    }
    //—————————————————————————

    //—————————————————————————
    class TTestThread : public TThread
    {
    private:
    protected:
    void __fastcall Execute();
    public:
    __fastcall TTestThread(bool CreateSuspended);
    };
    //—————————————————————————
    __fastcall TTestThread ::TTestThread (bool CreateSuspended)
    : TThread(CreateSuspended)
    {
    FreeOnTerminate = true;
    }
    //—————————————————————————
    void __fastcall TTestThread ::Execute()
    {
    Test();
    }
    //—————————————————————————
    // test code based on post from https://ibeblog.com/2010/08/17/delphi-thread-variables/
    // this was a piece of Delphi code which I rewrote in C++ for Borland
    //—————————————————————————
    #pragma argsused
    int main(int argc, char* argv[])
    {
    i = 0;
    printf(“main() calls Test().\n”);
    Test(); // after the first call to Test, TestVar should be “Asdf0”
    printf(“Starting new TTestThread which calls Test().\n”);
    // create thread
    new TTestThread(false); // create and call Test()

    // do it again
    printf(“Starting new TTestThread which calls Test().\n”);
    new TTestThread(false); // create and call Test()

    printf(“TestVar at main() exit (should be Asdf0) %s\n”, TestVar); // after 3 calls to Test, this TestVar should still be “Asdf0”
    //
    // All values for &TestVar should be different
    //
    return 0;
    }
    //—————————————————————————

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.