If you have a clean set of Delphi code, and all threads are created using TThread, you could set a breakpoint in the constructor method(s) (TThread.Create) and find out who created your threads. You could even try to name all your threads using the feature built into the Delphi TThread object that lets you set a debug-name for each thread.
But how do you identify the persistent, hard-to-find extra threads, that are still anonymous (no debug name) and which appear, say, during module initialization time, as the application starts. I can single-step through module-initialization, but I am unable to determine all the source modules (out of say, 900+ module initialization sections that are done) that are likely to create threads, and I have not figured out a way to add a debug-message (using breakpoint properties and messages) that would dump each unit name while it initializes. Creative use of breakpoints set in System.pas, with logging-messages allows me to do some things when debugging trivially simple applications, but the more complex my application grows, the more I feel blind-sided by threads, both those created during the middle of an application runs, and those created at module-init time (that is before you step into the first line of the code in your project dpr).
I would like to know what advanced techniques you might have found to identify and figure out who created a particular thread. If we were using a debugger like GDB instead of a debugger like the delphi debugger kernel (Turbo Debugger?) that is built into the delphi IDE, I think we could set a breakpoint on a windows api function like BeginThread itself. But I don't think I can do that in Delphi.
Update: I didn't know you could set a breakpoint in the implementation section of windows.pas for external windows dlls like kernel32.dll.
Update 2: It seems that David H's answer is the best idea for general use. I also am looking into a little helper code library that I'm writing right now that maintains a dictionary of thread ids that have been seen before, and which assigns some debug names to otherwise unnamed threads, based on their time of creation (what function we were calling just before we notice the new thread exists). I think this will help me to narrow down my 40+ numbered threads so that they all get named, even though some of them are created in external C/C++ dlls, or by COM processes.