【问题标题】:how to let the TThread to keep working in background and showing me the result in a Listbox?如何让 TThread 继续在后台工作并在 Listbox 中显示结果?
【发布时间】:2012-07-09 13:40:37
【问题描述】:

我必须开发一个程序来根据我给出的 Select 语句持续观察数据库中的值

监视的值可以随时更改,我的程序必须根据我给出的 select 语句的结果来感知更改

我想通过使用 TThread 来查看选择结果,因为我的系统还有其他功能,用户需要对其进行处理,而不仅仅是查看值。

如何在 Delphi XE2 中使用 TThread

我正在使用 VCL...没有 .Net

问候。

【问题讨论】:

  • 从线程中调用TThread.Synchronize 来更新用户界面。这会在主 UI 线程上运行更新代码。
  • Delphi 附带了一个简单的线程应用程序,它很好地展示了如何使用同步来更新主线程中的 UI 控件。
  • 好的。那么你在任务的哪一部分遇到了麻烦? “怎么做”不够具体。
  • @David,在使用项目填充 Listbox 的情况下,不需要与主线程同步,因为 Listbox 使用 SendMessage 来“填充”自己的项目。当不需要与主线程显式同步时,这是罕见的例外之一。
  • @iamjoosy 我不这么认为。如果访问列表框Handle 会导致RecreateWnd。此时列表框与错误的线程有关联。

标签: delphi tthread


【解决方案1】:

[编辑]

改进的答案,现在线程连续运行并“保持观察值”。

让我们构建一个示例。

首先,创建新的 VCL 应用。在窗体上放置一个 TListBox 和两个 TButton 组件。您需要编写按钮单击处理程序并添加一种私有方法。整个单元应如下所示:

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
    FCollector: TCollector;
    procedure OnCollect(S: TStrings);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Assigned(FCollector) then Exit;
  FCollector := TCollector.Create;
  FCollector.OnCollect := Self.OnCollect;
  FCollector.Start;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if not Assigned(FCollector) then Exit;
  FCollector.Terminate;
  FCollector := nil;
end;

procedure TForm1.OnCollect(S: TStrings);
begin
  ListBox1.Items.AddStrings(S);
end;

end.

接下来我们应该添加我们的线程,从菜单中选择:文件 -> 新单元并替换为代码:

单元单元2;

interface

uses
  System.Classes;

type

  TCollectEvent = procedure (S: TStrings) of object;

  TCollector = class(TThread)
  private
    { Private declarations }
    FTick: THandle;
    FItems: TStrings;
    FOnCollect: TCollectEvent;
    FInterval: Integer;
  protected
    procedure Execute; override;
    procedure DoCollect;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Terminate;
    property Interval: Integer read FInterval write FInterval;
    property OnCollect: TCollectEvent read FOnCollect write FOnCollect;
  end;

implementation

uses Windows, SysUtils;

{ TCollector }

constructor TCollector.Create;
begin
  inherited Create(True);
  FreeOnTerminate := True;
  FInterval := 1000;
  FTick := CreateEvent(nil, True, False, nil);
end;

destructor TCollector.Destroy;
begin
  CloseHandle(FTick);
  inherited;
end;

procedure TCollector.DoCollect;
begin
  FOnCollect(FItems);
end;

procedure TCollector.Terminate;
begin
  inherited;
  SetEvent(FTick);
end;

procedure TCollector.Execute;
begin
  while not Terminated do
  begin
    if WaitForSingleObject(FTick, FInterval) = WAIT_TIMEOUT then
    begin
      FItems := TStringList.Create;
      try
        // Collect here items
        FItems.Add('Item ' + IntToStr(Random(100)));
        Synchronize(DoCollect);
      finally
        FItems.Free;
      end;
    end;
  end;
end;

end.

现在,当您按下 Button1 时,您将收到来自组合中的线程项目。按下 Button2 线程停止执行。

你应该考虑到:

  1. 设置Interval为观察值,默认1000ms,见interval属性;

  2. 您在 Execute 中并与之相关的所有代码(包括数据库访问组件)都应该是线程保存的。

【讨论】:

  • 您所说的“您在 Execute 中并与之相关的所有代码(包括数据库访问组件)都应该是线程保存的”是什么意思?
  • 关于ADO的一些注意事项:stackoverflow.com/questions/3266532/…
  • 嗨,就像您提供的一样 (stackoverflow.com/questions/3266532/…),他们写道,每个线程都需要自己的连接,必须在线程执行中创建......但在我的情况下,我只有一个连接,因为所有请求的记录都位于一个数据库中,但位于不同的表中,因此在我的情况下,每个线程都必须有自己的 ADODataSet 并且这些数据集可以连接到一个 ADOConnection!...不是以这种方式工作吗?我试过了,没有错……
  • 我还注意到一些奇怪的事情,即当我终止线程时它不会中断执行,直到它完成之后它停止工作......我应该调用什么来停止线程的执行任何一点,即使该过程尚未完成...谢谢。
  • 要终止线程,你应该调用 Thread.Terminate;请参阅答案中的 Button2Click。
猜你喜欢
  • 2013-02-16
  • 2014-01-21
  • 2022-10-19
  • 2020-03-05
  • 2019-11-25
  • 2019-08-30
  • 1970-01-01
  • 1970-01-01
  • 2020-12-27
相关资源
最近更新 更多