【问题标题】:FireDac freezes GUIFireDac 冻结 GUI
【发布时间】:2016-06-15 08:48:45
【问题描述】:

我在 Delphi 10.1 Berlin 下使用 FireDac。

为了向用户显示数据,我使用 TDBEdit 等数据感知控件。

我使用 TFDQuery 和 TDataSource 将它们与控件链接起来。

这可行,但需要一些时间执行的长 sql 查询会冻结 GUI。

我想知道如何在执行那些长时间运行的查询时阻止 gui 冻结。

我在考虑后台线程。

在 wiki 上,我读到 FireDac 可以使用多线程: http://docwiki.embarcadero.com/RADStudio/XE6/en/Multithreading_(FireDAC)

然而在 embarcadero 社区论坛threadJeff Overcash 写道:

我没有看到有人问或 Dmitry 提到的一件事是你不能拥有 TDataSource 或 LiveBindings 针对您的后台线程查询。 如果您正在后台处理显示结果的查询 应该断开 LB 或 DataSource,打开并获取所有数据 然后重新建立连接。

这两个将尝试将光标移动到您身上或查询 用于显示的缓冲区,而缓冲区非常不稳定正在移动 在不同的线程中。

我想知道是否有人也使用 FireDac 并在表单上显示值可以帮助我。

【问题讨论】:

  • 您是否尝试过在后台线程执行期间断开数据源/实时绑定?如果我正确理解了报价,这应该可行。
  • 试过了。但是,断开连接将清空所有表单字段,因为 TFDQuery 不再处于活动状态并重置 TDataSource,因此如果您使用的是 DBNavigator,则您不在同一位置。
  • 如果您使用 TClientDataSet 执行此操作,请将其称为 CDS1,连接到 db-aware gui 组件,这样做的一种方法是使用第二个 CDS,CDS2,它在后台线程,然后一旦查询完成,调用 Synchronize 并将其 Data 属性分配给 CDS1 - 这就是它所需要的,并且分配将是即时的。我想在 FireDAC 中也可以做类似的事情。
  • 可以保存和恢复结果集中的位置(使用书签)

标签: delphi concurrency firedac delphi-10.1-berlin


【解决方案1】:

下面的代码示例显示了一种从 MSSql 服务器检索记录的方法 在使用 FireDAC 的后台线程中。这省略了一些细节。例如,在实践中,不是 TQueryThreads Execute 只打开一次查询然后终止,您可能希望线程的 Execute 包含一个 while 循环,在该循环中它在调用Synchronize 然后关闭/重新打开查询以根据需要随时更新主线程。

type

  TForm1 = class;

  TQueryThread = class(TThread)
  private
    FConnection: TFDConnection;
    FQuery: TFDQuery;
    FForm: TForm1;
  published
    constructor Create(AForm : TForm1);
    destructor Destroy; override;
    procedure Execute; override;
    procedure TransferData;
    property Query : TFDQuery read FQuery;
    property Connection : TFDConnection read FConnection;
    property Form : TForm1 read FForm;
  end;

  TForm1 = class(TForm)
    FDConnection1: TFDConnection;
    FDQuery1: TFDQuery;
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    DBNavigator1: TDBNavigator;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  public
    QueryThread : TQueryThread;
  end;

[...]

constructor TQueryThread.Create(AForm : TForm1);
begin
  inherited Create(True);
  FreeOnTerminate := True;
  FForm := AForm;
  FConnection := TFDConnection.Create(Nil);
  FConnection.Params.Assign(Form.FDConnection1.Params);
  FConnection.LoginPrompt := False;

  FQuery := TFDQuery.Create(Nil);
  FQuery.Connection := Connection;
  FQuery.SQL.Text := Form.FDQuery1.SQL.Text;
end;

destructor TQueryThread.Destroy;
begin
  FQuery.Free;
  FConnection.Free;
  inherited;
end;

procedure TQueryThread.Execute;
begin
  Query.Open;
  Synchronize(TransferData);
end;

procedure TQueryThread.TransferData;
begin
  Form.FDQuery1.DisableControls;
  Form.FDQuery1.Data := Query.Data;
  Form.FDQuery1.EnableControls;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  QueryThread.Resume;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  QueryThread := TQueryThread.Create(Self);
end;

MJN 关于书签的评论告诉您如何在 gui 中保留当前数据行位置。

顺便说一句,虽然我经常使用 TClientDataSets 完成此操作,但将这个答案放在一起是我第一次尝试使用 FireDAC。在配置组件方面,我所做的只是将组件拖出 Palette,按照您的预期“将它们连接在一起”,然后设置 FDConnection 的 Params 和 FDQuery 的 Sql。

【讨论】:

  • 感谢@MartynA,感谢您花时间回答这个问题。然而,我只是在 emba wiki 上偶然发现了这个网站:docwiki.embarcadero.com/Libraries/XE8/de/…。我将 CmdExecMode 执行模式更改为“amNonBlocking”,现在 gui 不再冻结。
  • 所以在我的情况下可能不需要后台线程,而 CmdExecMode 足以满足我的需求。我仍然不确定我是否遇到了其他问题,所以我必须做更多的测试。
  • 很高兴您找到了一个似乎对您有用的替代答案。
猜你喜欢
  • 2013-01-16
  • 2020-08-06
  • 2015-04-08
  • 2015-05-09
  • 1970-01-01
  • 2012-06-26
  • 2015-03-05
  • 2014-04-09
相关资源
最近更新 更多