【问题标题】:Delphi - Can a TThread change the value of variable in main VCL thread?Delphi - TThread 可以更改主 VCL 线程中的变量值吗?
【发布时间】:2015-04-03 12:36:14
【问题描述】:

使用:Delphi XE2,Windows VCL Forms 应用程序

TThread 可以在其执行期间更改主 VCL 线程中变量的值吗?

需要更新一个 Integer,它被声明为 TForm 类的一个字段。它将作为重载(并重新引入)的 Create 构造函数方法中的 var 变量传递给 TThread。

这有什么退路吗?

【问题讨论】:

  • 有数以千计的关于这个主题的文章、问题和教程。到目前为止你发现了什么?您将此变量传递到线程的方法是错误的。您需要同步事件来更改值,或使用消息等其他方法。
  • 我也不确定如何将 var 参数传递给线程的构造函数将允许线程更新该变量(除了在构造函数中)。您必须有一个指向该整数的指针才能执行此操作,这仍然不安全,因为该变量是 VCL 的一部分。
  • Thread-safe in delphi 的可能重复项
  • @JerryDodge 如果 int 只是 Form 中的一个字段,那么从技术上讲它不是 VCL - 它只是一个类字段。临界区或其他同步 obj 就可以了。
  • 不要使用 reintroduce:1) 该功能是为特别狭窄的用例添加的。即使在那里,人们也应该尽一切努力隐藏祖先方法,而不是隐藏存在问题的事实。 2)在您的特定情况下,您的线程需要获得所有相关信息才能完成其工作。 TThread.Create 不是虚拟的。所以宁愿声明一个单一的股票标准constructor Create(<required params>);,就是这样。编写线程构造函数以获取所需的一切:不多不少。这样维护起来会容易得多。

标签: delphi tthread


【解决方案1】:

是的,线程可以修改变量。变量不属于线程。变量可以属于表单或线程对象,但线程对象(即TThread 或其后代的实例)与操作系统执行线程不同。

对象可以具有在多个线程中运行的代码。您的 TThread.Create 方法在调用它的线程的上下文中运行,这通常是您的主线程。另一方面,Execute 方法在创建的 OS 线程的上下文中运行。但是很明显,这两种方法都可以访问TThread对象的字段,这就回答了两个操作系统线程是否可以访问同一个变量的问题。

但是,您将无法以您描述的方式访问表单变量。将它作为 var 参数传递给构造函数将允许 constructor 修改它,但正如我上面提到的,构造函数不会在新 OS 线程的上下文中运行。要允许新线程访问该变量,您需要存储一个指向它的指针,而不是通过引用传递它。例如:

type
  TSteveThread = class(TThread)
  private
    FVariable: PInteger;
  protected
    procedure Execute; override;
  public
    constructor Create(Variable: PInteger);
  end;

constructor TSteveThread.Create;
begin
  inherited Create(False);
  FVariable := Variable;
end;

procedure TSteveThread.Execute;
begin
  // Access FVariable^ here.
end;

像这样创建它:

procedure TSteveForm.ButtonClick;
begin
  TSteveThread.Create(@Self.Variable);
end;

另一种方法是传递对表单的引用,然后通过该引用访问表单的字段。例如:

type
  TSteveThread = class(TThread)
  private
    FForm: TSteveForm;
  protected
    procedure Execute; override;
  public
    constructor Create(Form: TSteveForm);
  end;

constructor TSteveThread.Create;
begin
  inherited Create(False);
  FForm := Form;
end;

procedure TSteveThread.Execute;
begin
  // Access FForm.Variable here.
end;

像这样创建它:

procedure TSteveForm.ButtonClick;
begin
  TSteveThread.Create(Self);
end;

在任何一种情况下,您都需要采取通常的预防措施来控制多个线程同时访问数据。底线是两个线程都可以访问数据。

【讨论】:

  • 我被甩了一秒钟,Create 的声明有参数但Implementation 部分没有。然后我想起来了,虽然它令人困惑,但它是有效的代码。为避免混淆,我总是使 Implementation 的签名与对象中声明的完全匹配。
  • 您的第二个替代解决方案不是主要违反规则吗?这是一个如何不这样做的例子。
  • 我也是,@Jerry,但我正在手机上输入此内容,所以我不想打扰超出我需要的内容。
  • 那是工作线程直接改变VCL主线程,根本没有任何保护。这就是导致竞争条件的原因。事实上,第一个例子也没有保护,比如临界区。
  • 如果两个线程有​​可能在没有其他考虑的情况下同时使用它,这只是一个竞争条件。可以使用关键部分、联锁操作和任何数量的其他上下文相关技术来避免它,我在最后一段中提到了这一点。我的两个例子在这方面完全没有区别,@Jerry。
猜你喜欢
  • 1970-01-01
  • 2015-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-18
  • 2011-03-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多