由于数据比较多,
如果不用线程来读取则会出现界面长时间假死的状态,非常不爽,用线程后会出现一些奇怪的现象:
 调用线程后,DBGrid会闪一下,然后要拖动滚动条才可以看到显示不正常的数据;
          具体表现为: 列宽不对/很多区域为空白,好像没有数据一样,
// 线程类的定义与实现:
type
  TLoadDataThrd = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
     procedure SetGridWidth;
  public
    adoConn : TADOConnection;
    SQLString:WideString;
    DBGridTemp:TDBGrid;
    adsData:TADODataSet;
    dsData:TDataSource;
end;{ TLoadDataThrd }
procedure TLoadDataThrd.SetGridWidth;
var
  i : integer;
begin
  for i := 0 to DBGridTemp.Columns.Count-1 do
  begin
    DBGridTemp.Columns[i].Width := 70;
  end;
end;procedure TLoadDataThrd.Execute;
begin
  adsData.Close;
  adsData.Connection := adoConn;
  adsData.CommandText := SQLString;
  try
    adsData.Open;
    if adsData.Active then
    begin
      SetGridWidth;// 虽然此处调用了此函数来设置Grid列宽度,但是最后显示出的宽度根本不正确!
    end;
  finally
  end;
end;// 在窗口类中实现调用线程
{TfrmData}
//function TfrmData.getSQLCmd:WideString; // 获取 sql语句的函数,返回如:select * from .....
// adoConn1:TAdoConnection;
// DBGridTest:TDBGrid;
// adsTest:TADODataSet;
// dsTest:TDatasource;
function TfrmData.RunThread(adoConnTemp:TADOConnection; SQLString : widestring; 
                        DBGridTemp:TDBGrid; adsTemp: TADODataSet; dsTemp : TDataSource): TLoadDataThrd;
var
  loadThread:TLoadDataThrd;
begin
  loadThread := TLoadDataThrd.Create(True);
  loadThread.adoConn := adoConnTemp;
  loadThread.DBGPM := DBGridTemp;
  loadThread.adsData := adsTemp;
  loadThread.dsData := dsTemp;
  loadThread.SQLString := SQLString;
  loadThread.Resume;  Result := loadThread;
end;// 此处调用线程取数据库数据
procedure TfrmData.btnSearchClick(Sender: TObject); 
begin
  RunThread(adoConn1,getSQLCmd,DBGridTest,adsTest,dsTest);
end;

解决方案 »

  1.   

    因VCL不是线程安全的,在线程中不能操作主线程的界面。下面是本人一个测试线程查询的代码。在线程中操作主线程界面需要使用Synchronize()同步。
    unit TQueryThreadUnit;interfaceuses
      Classes,ADODB,DB,ActiveX,ComCtrls;type
      TQueryThread = class(TThread)
      private
        FDataSet :TADODataSet;
        FDataSource :TDataSource;
        FStatusBar :TStatusBar;
        procedure HookUpUI;
        { Private declarations }
      protected
        procedure Execute; override;
      public
        constructor Create(ADataSet:TADODataSet;ADataSource:TDataSource;AStatusBar:TStatusBar);virtual;
      end;implementation{ Important: Methods and properties of objects in visual components can only be
      used in a method called using Synchronize, for example,      Synchronize(UpdateCaption);  and UpdateCaption could look like,    procedure TQueryThread.UpdateCaption;
        begin
          Form1.Caption := 'Updated in a thread';
        end; }{ TQueryThread }
    constructor TQueryThread.Create(ADataSet:TADODataSet;ADataSource:TDataSource;AStatusBar:TStatusBar);
    begin
      inherited Create(True);
      FDataSet := ADataSet;
      FDataSource := ADataSource;
      FStatusBar := AStatusBar;
      FreeOnTerminate := True;
      Resume;
    end;procedure TQueryThread.Execute;
    begin
      { Place thread code here }
      CoInitialize(nil);
      try
        if not FDataSet.Active then FDataSet.Open;
        Synchronize(HookUpUI);
      finally
       CoUninitialize;
      end;
    end;procedure TQueryThread.HookUpUI;
    begin
      FDataSource.DataSet := FDataSet;
      FStatusBar.SimpleText := '已經打開數據表!';
    end;
    end.
      

  2.   

    是不是必须用procedure定义被Synchronize调用的函数?为什么我调用一个function时出错:
    [Error] UnitData.pas(122): There is no overloaded version of 'Synchronize' that can be called with these arguments
      

  3.   

    你的function怎么定义的,不是随便一个就可以
      

  4.   

    意义不大,涉及到VCL都要进行同步操作
    如果同步的时间很长则没有多大改变
    建议数据如果很多的话进行分页显示
      

  5.   

    一直以为 只能 ,procedure呢 ... 坐等讲解~~~
      

  6.   

    试一下这个:
    procedure TLoadDataThrd.Execute;
    begin
      adsData.Close;
      adsData.Connection := adoConn;
      adsData.CommandText := SQLString;
      try
        adsData.Open;
        if adsData.Active then
        begin
          Synchronize(SetGridWidth);// 虽然此处调用了此函数来设置Grid列宽度,但是最后显示出的宽度根本不正确!
        end;
      finally
      end;
    end;
      

  7.   

    Synchronize只能接收Procedure吧,而且Procedure不能带参数;需要传递数据的话可以向主线程发送个消息,主线程收到消息后处理~
      

  8.   

    函数定义如下:
    function SetGridWidth:Boolean;还有一个问题就是,假如我的function/procedure带有参数,该如何用Synchronize来调用?
    比如:
    function SetGridWidth(iCount:integer):boolean;
      

  9.   

    Synchronize中的过程不能有参数
    你可以把参数放在线程的局部变量里
    然后在Synchronize调用的过程里访问它们 
      

  10.   


    TMyThread=class(TThread)
    private
        FVar: Integer;
        procedure UpdateUI;
    ...
    procedure Execute; override;
    begin
        FVar:= 100;
        ..
        Synchronize(UpdateUI);
    end;procedure UpdateUI;
    begin
        Form1.caption:= Inttostr(FVar);
    end;类似这种吧,线程单元里引入主窗体单元,把FrmMain传进来,在UpdateUI里更新主窗体;其实没必要一定要用Synchronize,给主线程发消息,消息里面带参数,主线程收到消息处理参数也是可以的; 建议Google下 delphi+线程+UI,研究下看看~
      

  11.   

    线程读取前,将dbgrid 和数据集断开。。读完了,再连上。
      

  12.   

    用了Synchronize,之前的现象是没有了,不过,当窗口关闭时会出现一些莫名的错误,
    如:invalid pointer operation,
    Access violation at address ......
      

  13.   

    貌似是线程退出不正确,一般是访问null值造成,慢慢调试呗~
      

  14.   

    仔细检查也没发现访问null值的情况啊,网上有没有类似的例子可以推荐的?