Apr 222018
 

In Delphi, threads have traditionally been implemented as classes descending from TThread. While there have been quite a few improvements to multithreading, TThread is still the most compatible way. But TThread is not a normal class.

In a normal class, the constructor and destructor usually look like this:

constructor TSomeClass.Create;
begin
  inherited;
  FSomeList := TStringList.Create;
end;

destructor TSomeClass.Destroy;
begin
  FreeAndNil(FSomeList);
  inherited;
end;

In the constructor, we first call inherited and afterwards add code to initialize additional fields of the object. In the destructor we first free any fields of the object and only then call inherited for the ancestor class to free its resources.

Now, TThread is peculiar because its constructor and destructor are usually executed in the program’s main thread, while its Execute method is executed in a different thread. But, when is that thread started? It’s started when you call the inherited constructor. So any initialization that thread needs must be done before calling inherited!

Edit: Several people have pointed out that my claim is somewhat “outdated”, where “outdated” means that since Delphi 6 the thread is started in the AfterConstruction method rather than in Create. In Delphi 5 it was the last command in TThread.Create (here is a probably not authorized copy of that code, search for “Delphi5Thread” on the page).

What I wrote about the destructor is still true though.

constructor TMyThread.Create;
begin
  FSomeList := TStringList.Create;
  inherited;
end;

Otherwise the field FSomeList might not have been initialized when the thread tries to access it.

Similar, if the thread is still running, you must be sure that all the resources it requires are available until it has stopped. So, the first thing would be to tell the thread to terminate, wait for it to actually do that and only then destroy it.

MyThread := TMyThread.Create;
// do something else while the thread does its thing
// ...
// now tell it to terminate (this is standard way of doing that
// but it might not be the best)
MyThread.Terminate;
// and destroy it
FreeAndNil(MyThread);

But that’s only half of it. The destructor must also take care not to destroy anything the thread still needs:

destructor TMyThread.Destroy;
begin
  // first, wait for the thread to terminate
  // (the inherited destructor calls WaitFor)
  inherited;
  // now the thread has definitely terminated, so we can free the resources
  FreeAndNil(FSomeList);
end;

Beware: These are just the simplest precautions to take when programming with multiple threads. As they say: "Creating threads is easy but multithreading is hard." If you can, use a library that does the ground work for you.

Why do I blog about this? I just fixed a few Access Violations in GExperts that were caused by TThread objects implemented the wrong way. (I have no idea who did that, it’s quite possible that I am myself to blame for that.)