命名线程的最佳实践

4
最近我在工作中接触了高并发的事件驱动框架(Java Akka),该框架会创建大量的Actor线程。当我调试Akka应用程序时,线程名称非常有意义,并且感觉非常棒。但是回到Delphi编程时,我感到很失落,因为所有线程都没有名称,即使它们在过去的20年里一直没有名称。
对于我自己设计的所有线程类,我都遵循一个模式:定义一个setter方法SetThreadName,并在Execute方法中调用NameThreadForDebugging。到目前为止,这个方法运行得很好。
type
  TMyThread = class(TThread)
  private
    FThreadName: string;
  protected
    procedure Execute; override;
  public
    procedure SetThreadName(const ThreadName: string);
  end;

procedure TMyThread.SetThreadName(const ThreadName: string);
begin
  FThreadName := ThreadName;
end;

procedure TMyThread.Execute;
begin
  NameThreadForDebugging(FThreadName);
  // Put normal thread execution code here
end;

但是那些第三方线程的实例将保持匿名,除非我创建一个合适的线程类。有“Delphi Magic”可以将 `SetThreadName` 设置为基本 Thread 类吗?我可以使用 `Detour.pas` 强制在 `Execute` 方法的第一步调用 `NameThreadForDebugging(FThreadName)`。
有什么想法吗?
更新1:感谢 David 的友善帮助。为了帮助其他读者更好地理解我的问题,问题已经稍作改述。
1. 我的代码哪里出错了? NameThreadForDebugging 方法实际上是一个静态方法。第二个参数 ThreadId 是可选的,默认情况下它等于当前线程 id。如果我没有清楚地给出 ThreadId,很可能会给当前线程命名,而不是我真正想要命名的线程。
2. 解决方案是什么? 在任何地方调用 `MyThread.NameThreadForDebugging('a_name', MyThread.ThreadId);` 或在 `TMyThread.Execute` 开始时调用 `NameThreadForDebugging('a_name');`。
3. 为什么让事情变得如此困惑? 我不明白为什么不提供一个没有第二个 ThreadId 的非静态版本。如果有这样一个非静态版本,我就不会犯这个错误了。

FThreadName是什么?那些不受您控制的第三方组件将如何命名它们的线程?挂钩是容易的部分,但获取名称则不是那么简单。 - David Heffernan
如果我提供了一个 SetThreadName,它将会给私有变量 FThreadName 设置一个名称。 - stanleyxu2005
我不理解最后那条评论。谁会提供这些名称,以及在什么时候会发生这种情况? - David Heffernan
1
关于您的编辑,绕路很容易做到。嗯,当然可能,我们不用担心如何做到这一点。虽然我不认为我会绕路TThread,因为有其他方法可以创建线程。但是我不知道谁会实际设置FThreadName。更不用说助手不能添加数据成员,只能添加方法了。您似乎仍然没有理解我的观点。如果任何人都能够为线程提供名称,那么他们就能够直接调用NameThreadForDebugging - David Heffernan
1个回答

7

我在这里临时发挥,但是我认为你认为只有在执行代码的线程内部才能命名线程。但事实并非如此。要命名线程,您只需要知道其ID。

文档给出了函数签名:

class procedure NameThreadForDebugging(AThreadName: AnsiString; 
  AThreadID: TThreadID = TThreadID(-1)); static;

如果您不提供可选的线程ID参数,则会传递-1,这被解释为意味着“执行线程”。这就是迄今为止使用NameThreadForDebugging的方式。但是,您可以直接传递线程ID。由于您明显拥有线程实例,因此也拥有它们的ID。
您想象的接口涉及调用线程的实例方法,并传递线程的名称。也就是说,您想编写以下代码:
Thread.SetThreadName(ThreadName);

不必这样做,你可以简单地写成:

TThread.NameThreadForDebugging(ThreadName, Thread.ThreadID);

如果您想使用类助手,可以按照以下方式进行操作:
type
  TThreadHelper = class helper for TThread
  public
    procedure SetThreadName(const ThreadName: string);
  end;

procedure TThreadHelper.SetThreadName(const ThreadName: string);
begin
  TThread.NameThreadForDebugging(ThreadName, ThreadID);
end;

坦白地说,我不认为我会去做这样的麻烦事。调用NameThreadForDebugging似乎完全足够。

哦,是的!感谢您的热心帮助。我怎么会这么糊涂呢? - stanleyxu2005
好吧,我花了很长时间才理解这个问题! - David Heffernan
如果 NameThreadForDebugging 不是静态方法,它可以帮助在预期的线程上下文中设置线程名称。 - stanleyxu2005
2
它被定义为类静态方法的原因正是为了能够在没有 TThread 实例的情况下调用它。这使您能够命名不是 TThread 的子类的线程。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接