【问题标题】:How would one pass a dynamic property as a parameter to a function?如何将动态属性作为参数传递给函数?
【发布时间】:2017-06-04 14:08:48
【问题描述】:

假设我有一个对象,在我的例子中是一个 TThread,我想在一个函数中动态检查它的一个属性,在这个例子中是 TThread.Terminated。

是否有一种干净的方式来传递“属性”,这样我实际上是在传递 getter 函数,因此可以动态检查该属性的值?

更多细节:

我的具体问题是我第一次在 Delphi 中实现线程。我不喜欢标准的 Delphi 线程实现似乎是创建一个自定义线程类来执行您想要线程化的方法。我找到了一个令人满意的解决方案来解决这个烦恼,其中使用委托来定义要执行的方法,然后在创建时将实际方法传递给线程对象,从而更好地分离/封装线程代码和要执行的方法线。详情如下。

但是,我发现上述想法的实现使用 TThread.Suspend 来允许提前终止正在执行的方法。 Delphi 文档将 TThread.Suspend 标记为已弃用,而是建议长时间运行的方法应检查 Terminated 属性,以便在父线程请求时允许提前终止。因此,我需要一种方法将对 TThread.Terminated 的引用传递给它正在执行的方法,以便该方法可以实现它自己的提前终止。

我无法使用 var 语法通过引用传递属性。 我不能通过引用传递底层 FTerminated 字段,因为它是 TThread 的私有成员。 我知道我可以将 TThread 对象的引用传递给该方法,但这种循环引用似乎是解决问题的一种老套方法。

那么,是否有一种“可接受”的方式来动态传递属性,还是我坚持使用当前的方法?

当前代码: 界面

uses
  Windows, Messages, Classes, Forms;

type
  // Method Thread Will Execute. Acts on Data. Runs in RunningThread.
  // (Reference to RunningThread is past so method can check runningthread.terminated
  //    to allow long running methods to terminate early when parent thread requests it)
  TThreadMethod = procedure (Data: pointer; RunningThread: TThread) of object;
  //A Simple Thread Which Excecutes a Method on Some Data
  TSimpleThread = class(TThread)
    protected
       Method: TThreadMethod;
       Data: pointer;
       procedure Execute; override;
    public
       constructor Create(Method: TThreadMethod; Data: pointer; CreateSuspended: boolean); overload;
   end;

implementation
  // SimpleThread
  constructor TSimpleThread.Create(Method: TThreadMethod;
                                Data: pointer;
                                CreateSuspended: boolean );
   begin
     Self.Method := Method;
     Self.Data := Data;
     inherited Create(CreateSuspended);
     Self.FreeOnTerminate := True;
   end;

  procedure TSimpleThread.Execute();
   begin
     Self.Method(Data, Self);
   end;

【问题讨论】:

  • 让你的线程类实现一个你传递给你的线程执行方法的接口。
  • - “我不能通过引用传递底层 FTerminated 字段,因为它是 TThread 的私有成员。” - 不是我推荐的,但是你可以因为吸气剂只读取该字段。请参阅this post 下的讨论。

标签: delphi delphi-7


【解决方案1】:

假设我有一个对象,在我的例子中是一个 TThread,我想在一个函数中动态检查它的一个属性,在这个例子中是 TThread.Terminated。

处理这个问题的最好方法是从TThread 派生一个新类并覆盖其虚拟Execute() 方法以直接运行您的线程代码,然后该代码可以在需要时访问对象的Terminated 属性。正如您的示例所做的那样。

如果您的线程代码不能直接放入Execute()(例如,您正在使用TThread.CreateAnonymousThread()TTask),您不能将原始TThread 对象指针直接传递给线程过程,一切都没有丢失。 TThread 有一个 CurrentThread 类属性来访问正在运行调用代码的 TThread 对象。当TThread 开始运行时,它会将其Self 指针存储在thread 变量中(因此每个线程上下文都有自己的变量副本),然后CurrentThread 属性会访问该变量。

但是,您的问题被标记为 Delphi 7,它不支持匿名过程、匿名线程或任务,因此您基本上只能手动将 TThread 对象传递给您的过程代码。如果您不想将对象指针作为过程参数传递,您可以使用自己的 thread 变量。

是否有一种干净的方法来传递“属性”,这样我实际上是在传递 getter 函数,因此可以动态检查该属性的值?

对于初学者来说,没有 getter 函数,Terminated 属性指示返回 FTerminated 成员,无论如何它是 private。您需要实际的 TThread 对象指针才能读取 Terminated 属性。

传递指向FTerminated 成员本身的指针/引用的唯一方法是使用Extended RTTI,这在Delphi 7 中不存在。所以这对您没有帮助。 Delphi 7 中的旧 RTTI 不够丰富,无法访问 FTerminated 成员。

但是我发现上述想法的实现使用TThread.Suspend 允许提前终止正在执行的方法。

那么你正在使用一个糟糕的实现。

除非您指的是TThread 构造函数的ACreateSuspended 参数,否则使用起来非常安全。如果设置为 True,则不会创建线程然后挂起,而是在开始时以挂起状态创建线程。当您准备好在创建线程后启动线程时,您只需调用Resume()(或更高版本的Delphi 版本中的Start())即可。

否则,只需将ACreateSuspended设置为False,并让它在所有构造函数退出后自动启动。

Delphi 文档将 TThread.Suspend 标记为已弃用

没错。 Suspend() 从线程外部使用永远不会安全,因为其他线程无法确保线程处于挂起的安全状态。 Suspend() 仅在线程从其自己的 Execute() 方法中挂起自身时才能安全使用,因为只有该线程知道何时可以安全地执行挂起。但即便如此,通常还有更好的方法来实现线程挂起,而无需实际使用Suspend()(例如等待TEvent)。

因此,我需要一种方法将对 TThread.Terminated 的引用传递给它正在执行的方法,以便该方法可以实现它自己的提前终止。

不,您需要将对 TThread 对象本身的引用传递给您的过程,而不是对任何特定属性的引用。

我无法使用 var 语法通过引用传递属性。我不能通过引用传递底层 FTerminated 字段,因为它是 TThread 的私有成员。

正确。

我知道我可以将 TThread 对象的引用传递给方法,但这种循环引用类型似乎是解决问题的一种老套方法。

如果您想使用 TThread.Terminated 属性,这是唯一的方法。

否则,根本不要使用TThread.Terminated。请改用您自己的信号机制。 Terminated 属性只是一个 Boolean 标志,仅此而已。它是作为方便而不是要求提供的。您可以使用任何您想要的信号来指示您的过程退出并让线程自行终止。

那么,是否有一种“可接受”的方式来动态传递属性,还是我坚持使用当前的方法?

您当前的方法基本上是您必须做的,尤其是在 Delphi 7 中。对于以后的 Delphi 版本,您基本上是在重新创建 TThread.CreateAnonymousThread() 所做的事情,例如:

TThread.CreateAnonymousThread(
  procedure
  begin
    MyMethod(MyData, TThread.CurrentThread);
  end
).Start;

【讨论】:

  • 他想制作自己的CreateAnonymousThread 版本。完全有可能。甚至不需要从 TThread 派生。
  • 为什么你认为不需要派生类? TThread 不支持在没有 CreateAnonymousThread() 的 Delphi 版本中运行用户提供的方法。
  • 您可以使用 BeginThread 例如。如果你想。他只是不想为每个任务派生一个新类。
  • 感谢 Remy 的深入分析,这正是我所希望的。在我深入之前仔细检查我的假设非常有帮助。 (也感谢你和大卫设法解释了我糟糕的解释。)
猜你喜欢
  • 2018-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-18
  • 2014-03-24
  • 1970-01-01
  • 2012-02-13
相关资源
最近更新 更多