【问题标题】:Delphi XE8 - Share variables between threadsDelphi XE8 - 在线程之间共享变量
【发布时间】:2015-10-30 15:45:32
【问题描述】:

我想知道是否有可能出现这种情况:

1) 有一个带有一些变量的主线程。

2) 这个线程必须创建 2 个子线程,并且这个线程应该能够读取主线程的变量。

3) 我想创建不同的主线程(带有子线程)。

这甚至可能吗?

到目前为止我已经尝试过了,但它不起作用(检查“procedure TSubThread.Execute;”):

unit uMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
  TMainThread = class(TThread)
  private
  protected
    procedure Execute; override;
  public
    TestVar1: integer;
  end;

  TSubThread = class(TMainThread)
  private
  protected
    procedure Execute; override;
  public
  end;

  TfMain = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    function RunThread(trTestVar1: integer): TMainThread;
    procedure ThreadTerminated(Sender: TObject);

    function RunSubThread: TSubThread;
    procedure SubThreadTerminated(Sender: TObject);
  public
    { Public declarations }
  end;

var
  fMain: TfMain;

implementation

{$R *.dfm}

////////////////////////////////////////////////////////////////////////////////
//                               MAINTHREAD
////////////////////////////////////////////////////////////////////////////////

function TfMain.RunThread(trTestVar1: integer): TMainThread;
var CalcThread : TMainThread;
begin
  CalcThread := TMainThread.Create(true);
  CalcThread.TestVar1 := trTestVar1;
  CalcThread.FreeOnTerminate := true;
  CalcThread.OnTerminate := ThreadTerminated;
  CalcThread.Start;
  Result := CalcThread;

  // Here I start the SUB-THREAD
  RunSubThread;
end;

procedure TfMain.ThreadTerminated(Sender: TObject);
begin
//
end;

procedure TMainThread.Execute;
begin
  inherited;
end;

////////////////////////////////////////////////////////////////////////////////
//                               SUBTHREAD
////////////////////////////////////////////////////////////////////////////////

function TfMain.RunSubThread: TSubThread;
var SubThread : TSubThread;
begin
  SubThread := TSubThread.Create(true);
  SubThread.FreeOnTerminate := true;
  SubThread.OnTerminate := SubThreadTerminated;
  SubThread.Start;
  Result := SubThread;
end;

procedure TfMain.SubThreadTerminated(Sender: TObject);
begin
  //
end;

procedure TSubThread.Execute;
begin
  inherited;

  // Here the value of TestVar1 is always 0
end;

////////////////////////////////////////////////////////////////////////////////
//                               MAIN THREAD
////////////////////////////////////////////////////////////////////////////////

procedure TfMain.FormCreate(Sender: TObject);
begin
  // I start the first "Main Thread"
  RunThread(1);

  // I start the second "Main Thread"
  RunThread(2);
end;

end.

【问题讨论】:

  • 在线程之间共享变量是微不足道的。您只需让它们可见,就像在对象之间共享变量一样。当然,现在您必须处理数据竞争。作为一般规则,共享是您希望最小化的事情。如果可能的话,根本不共享,然后你就没有争用了。
  • 您让我们注意TSubThread.Execute,其中一条评论说TestVar1 始终为0。这段代码中没有任何内容分配TestVar1,所以它唯一的值就是它的值在创建其封闭对象时获取,即 0。您希望它在TSubThread.Execute 或其他地方具有什么其他值?
  • 我希望我在 RunThread(1) 和 RunThread(2) 中传递的值会将该变量设置为 1 和 2。这只是一个测试,它不起作用......并且我希望得到一些建议..
  • 如果您不了解 Pascal 的基础知识,那么转向线程有点像刚获得初学者执照时试图参加 Indy 500 比赛。慢点,小辈。
  • 沃伦,我不明白你的意思..克里斯蒂安(下)理解了这个问题并解决了它..你写在这里是为了冒犯吗?

标签: multithreading delphi delphi-xe8


【解决方案1】:

让SubThread继承MainThread然后期望得到测试值是不对的。

我认为让子线程持有对主线程的引用要好得多,然后你会得到这样的结果:

unit uMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
  TMainThread = class(TThread)
  private
  protected
    procedure Execute; override;
  public
    TestVar1: integer;
  end;

  TSubThread = class(TThread)
  private
    FMainThread : TMainThread;
  protected
    procedure Execute; override;
  public
  end;

  TfMain = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    function RunThread(trTestVar1: integer): TMainThread;
    procedure ThreadTerminated(Sender: TObject);

    function RunSubThread(MainThread : TMainThread): TSubThread;
    procedure SubThreadTerminated(Sender: TObject);
  public
    { Public declarations }
  end;

var
  fMain: TfMain;

implementation

{$R *.dfm}

////////////////////////////////////////////////////////////////////////////////
//                               MAINTHREAD
////////////////////////////////////////////////////////////////////////////////

function TfMain.RunThread(trTestVar1: integer): TMainThread;
var CalcThread : TMainThread;
begin
  CalcThread := TMainThread.Create(true);
  CalcThread.TestVar1 := trTestVar1;
  CalcThread.FreeOnTerminate := true;
  CalcThread.OnTerminate := ThreadTerminated;
  CalcThread.Start;
  Result := CalcThread;

  // Here I start the SUB-THREAD
  RunSubThread(CalcThread);
end;

procedure TfMain.ThreadTerminated(Sender: TObject);
begin
//
end;

procedure TMainThread.Execute;
begin
  inherited;
end;

////////////////////////////////////////////////////////////////////////////////
//                               SUBTHREAD
////////////////////////////////////////////////////////////////////////////////

function TfMain.RunSubThread(MainThread : TMainThread): TSubThread;
var SubThread : TSubThread;
begin
  SubThread := TSubThread.Create(true);
  SubThread.FreeOnTerminate := true;
  SubThread.FMainThread := MainThread;
  SubThread.OnTerminate := SubThreadTerminated;
  SubThread.Start;
  Result := SubThread;
end;

procedure TfMain.SubThreadTerminated(Sender: TObject);
begin
  //
end;

procedure TSubThread.Execute;
var
  VarCopy : Integer;
begin
  inherited;

  VarCopy := self.FMainThread.TestVar1;

  // Here the value of TestVar1 is always 0
end;

////////////////////////////////////////////////////////////////////////////////
//                               MAIN THREAD
////////////////////////////////////////////////////////////////////////////////

procedure TfMain.FormCreate(Sender: TObject);
begin
  // I start the first "Main Thread"
  RunThread(1);

  // I start the second "Main Thread"
  RunThread(2);
end;

end.

【讨论】:

  • 哇!那行得通!这正是我所需要的!你觉得这样使用安全吗?
  • 最后一个问题,如果我从主线程更改TestVar1的值,子线程会看到变化吗?
  • 完美。进一步的成功很大程度上取决于您使用的是哪种数据结构。一个简单的整数就可以了。但是,如果您需要访问数组或类似的结果记录,很快就需要使用 TCriticalSection 或 InterlockedDecrement 等来确保数据一致性。是的,下一次子线程将“看到”变化是检查变量。
  • 太棒了!我实际上将使用布尔值和 varchar ......所以我应该没问题,但为什么使用数组我会遇到问题?我每个主线程只有一个子线程,一致性问题在哪里?
  • 如果主线程正在用新结果更新数组,并且这个数组没有锁定机制,没有什么可以阻止操作系统将控制权交给子任务还将读取相同的数组。这可能导致“一半”的结果。如果在更新期间更改数组的长度,则会发生奇怪的访问冲突。
【解决方案2】:

是的,可以在多个线程之间共享变量。它几乎是自动的。如果变量的作用域允许引用它,那么执行代码的任何线程都可以访问它。

在提供的代码中,没有一个线程可以通过任何方式相互引用。特别是,子线程没有引用TMainThread 的任何实例,因此它无法访问任何此类线程的TestVar1 成员。

【讨论】:

  • 我不知道它是否正确(可能不是),但是使用主线程类(TSubThread = class(TMainThread))创建子线程可以让我从子线程。
  • 问题是我第一次运行主线程时变量是 1,但第二次是 2。我需要在子线程中更改这两个值,但它总是 0
  • 每次调用RunMainThread,都会创建TMainThread的实例,然后调用RunSubThread,会创建TSubThread的实例。由于TSubThreadTMainThread 的后代,因此您实际上有两个 TMainThread 实例。第一个,你从trTestVar 分配了TestVar,第二个,它实际上是一个TSubThread 实例,你根本没有设置TestVar1。最终,您会得到四个 线程实例,并且每个 都有一个单独的TestVar1 成员变量。那不是分享。
猜你喜欢
  • 2018-07-01
  • 2021-01-27
  • 1970-01-01
  • 2017-07-22
  • 2019-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多