I haven’t heard of too many Delphi developers that insist on naming all threads created in an application, but I think it is good practice. Call me overly pedantic if you must, but the practice certainly has come in handy more than a few times over the years.
Now, there is a limit - I typically don’t dig into third party component code to name their threads. I simply haven’t had the need to do this (yet.)
Contributing to the issue is the few threads created by Delphi on application startup. There’s been a few posts over the years on what each of these threads do, but I’m not as interested as what they do but interested in tagging them as typically “not my problem.” I have written a handy utility method that will help. You simply include the unit in your Windows project and the few threads created automatically by Delphi will be named as such.
It’s surprisingly simple to do when using the Tool Help Library in the Windows API, which are nicely wrapped in the WinAPI.TlHelp32 unit. The full unit is below, and will also be added in the iaLib Github repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
unit iaWin.NameDelphiThreads;
interface
uses
WinAPI.Windows;
procedure NameDelphiThreads(const pMainThreadId:THandle);
implementation
uses
System.SysUtils,
System.Classes,
WinAPI.TlHelp32;
procedure NameDelphiThreads(const pMainThreadId:THandle);
var
vSnapshot:THandle;
vProcessId:THandle;
vTE32:TThreadEntry32;
i:Integer;
begin
vProcessId := GetCurrentProcessId();
vSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, vProcessId);
if vSnapshot <> INVALID_HANDLE_VALUE then
begin
try
vTE32.dwSize := SizeOf(vTE32); //If you do not initialize dwSize, Thread32First fails.
if Thread32First(vSnapshot, vTE32) then
begin
i := 1;
repeat
if vTE32.th32OwnerProcessID = vProcessId then
begin
if vTE32.th32ThreadID = pMainThreadId then
begin
TThread.NameThreadForDebugging('DelphiCreated_Main', pMainThreadId);
end
else
begin
TThread.NameThreadForDebugging('DelphiCreated_' + AnsiString(IntToStr(i)), vTE32.th32ThreadID);
Inc(i);
end;
end;
until not Thread32Next(vSnapshot, vTE32);
end;
finally
CloseHandle(vSnapshot);
end;
end;
end;
{$IFDEF DEBUG}
initialization
NameDelphiThreads(GetCurrentThreadId);
{$ENDIF}
end.
Simply put, instead of seeing something like this image below in your debugger Threads window:
You will now see something like the image below, with the Delphi-created threads clearly identified:
Granted, it is not an earth-changing difference, but you should ask yourself “How many times have you wondered what Thread Id 11376 is for?” Now, you may not know all the details on what Delphi is doing with each background thread, but you will now be able to separate the system-managed threads with the application-managed threads perhaps saving you a few headaches down the road.
The example code heavily follows Microsoft’s thread walking documentation. You simply create a snapshot of all threads for the CurrentProcessId and use the Thread32First and Thread32Next methods to process the list.
To name your own TThread descendants, use the TThread.NameThreadForDebugging method. Combine that practice with this unit’s functionality, you’ll have the Delphi-created threads named, all of your threads named, and then you can ponder on the remaining threads listed in the IDE and perhaps go on a fishing trip to determine their source. (See this StackOverflow question for assistance with that task.) The majority of the time, every item in the Threads list will now be named to make your debugging task just a little bit easier.
Update, June 2020
Short thread on DelphiPraxis forums: https://en.delphipraxis.net/topic/2677-do-you-name-your-threads-for-debugging
Ensure you use Ascii characters for thread name: RSP-17452
Named threads work great on Windows, but be aware of issues on other platforms. See iOS: RSP-29625, Linux: RSP-22083, Mac: RSP-26219