如题。使用多个线程查询数据,然后汇总为1个数据集。偶尔会丢掉一个线程查询的数据集。可能是什么原因呢。
代码很长,熟悉多线程的兄弟提示下,我贴相关的代码出来。
丢掉线程后,下次查询会出来如下错误。
'Access violation at address 770F4C27 in module 'oleaut32.dll'
没有丢掉线程时,无此错误提示。一共就5个线程而已。
确实是很奇怪的问题。
我已经仔细检查了数据集,在少数情况(大概十几次中出现1次)
有1个线程的数据集丢失。所有代码如下
=====线程的代码==================
unit QueryThread;
interface
    
uses   
    Classes,   Messages,   Provider,   ADODB,   ActiveX;const
    WM_QueryDone   =   WM_User+1010;type
    TTag   =   0..4;
    TQueryThread   =   class(TThread)
    private
        FTag:   TTag;
        FHandle:   THandle;
        FQuery:   TADOQuery;
        function   CreateSQL(const   Tag:   TTag):   string;
    protected
        procedure   Execute;   override;
    public
        constructor   Create(const   ADataSetProvider:   TDataSetProvider;   ATag:   TTag;   AHandle:   THandle);
    end;
    
implementationuses   
    Windows,   Forms,   SysUtils;   
    
{   TQueryThread   }   constructor   TQueryThread.Create(const   ADataSetProvider:   TDataSetProvider;
    ATag:   TTag;   AHandle:   THandle);   
begin
    FTag   :=   ATag;
    FHandle   :=   AHandle;
    FQuery   :=   TADOQuery.Create(nil);
    FQuery.ConnectionString   :=  ' Provider=SQLOLEDB.1;Password=111;Persist Security Info=True;User ID=sa;Initial Catalog=test';   //写入链接串
    FQuery.SQL.Text   :=   CreateSQL(FTag);   //创建SQL语句
    ADataSetProvider.DataSet   :=   FQuery;
    FreeOnTerminate   :=   True;   
    inherited   Create(False);
end;function   TQueryThread.CreateSQL(const   Tag:   TTag):   string;
begin
    case   Tag   of
    0:   Result   :=   'select top 5 * from VW_test where rq= ''2009-02-26 00:00:00.000''';   
    1:   Result   :=   'select top 5 * from VW_test where rq= ''2009-02-27 00:00:00.000''';  
    2:   Result   :=   'select top 5 * from VW_test where rq= ''2009-02-28 00:00:00.000''';   
    3:   Result   :=   'select top 5 * from VW_test where rq= ''2009-03-01 00:00:00.000''';   
    4:   Result   :=   'select top 5 * from VW_test where rq= ''2009-03-02 00:00:00.000''';  
    end;
end;procedure   TQueryThread.Execute;
begin
    CoInitialize(nil);
    try
        FQuery.Open;
        PostMessage(FHandle,   WM_QueryDone,   FTag,   0);   //发送查询结束消息
    finally
        CoUnInitialize;
    end;
end;
end.===========================调试的代码==
unit Unit1;interfaceuses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, DB, DBClient, Grids, DBGrids, Provider,
  QueryThread,ADODB,Contnrs;type
  TMainForm = class(TForm)
    BRefresh: TBitBtn;
    CDSLocal: TClientDataSet;
    CDSRemote_2: TClientDataSet;
    CDSRemote_1: TClientDataSet;
    CDSRemote_3: TClientDataSet;
    CDSRemote_4: TClientDataSet;
    DSPLocal: TDataSetProvider;
    DSPRemote_1: TDataSetProvider;
    DSPRemote_2: TDataSetProvider;
    DSPRemote_3: TDataSetProvider;
    DSPRemote_4: TDataSetProvider;
    DSLocal: TDataSource;
    DBGridLocal: TDBGrid;
    ADOConnection1: TADOConnection;
    BtnOneThread: TBitBtn;
    DS_OneThread: TDataSource;
    CDS_OneThread: TClientDataSet;
    ADODataSet1: TADODataSet;
    Edit1: TEdit;
    Btnrecord: TBitBtn;
    Edit_Record: TEdit;
    procedure BRefreshClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure BtnOneThreadClick(Sender: TObject);
    procedure BtnrecordClick(Sender: TObject);
  private
    { Private declarations }
    FQueue:   TQueue;   
    FThreadsRunning:   Integer;
    procedure   ThreadDone(Sender:   TObject);
    procedure   WMQueryDone(var   Msg:   TMessage);   Message   WM_QueryDone;  public
    { Public declarations }
  end;var
  MainForm: TMainForm;implementation{$R *.dfm}procedure TMainForm.BRefreshClick(Sender: TObject);
begin
      DBGridLocal.DataSource := DSLocal;      BRefresh.Enabled   :=   False;
      FThreadsRunning   :=   4;
      CDSLocal.Close;
      CDSRemote_1.Close;
      CDSRemote_2.Close;
      CDSRemote_3.Close;
      CDSRemote_4.Close;
      with   TQueryThread.Create(DSPLocal,   0,   Handle)   do
          OnTerminate   :=   ThreadDone;
      with   TQueryThread.Create(DSPRemote_1,   1,   Handle)   do
          OnTerminate   :=   ThreadDone;   
      with   TQueryThread.Create(DSPRemote_2,   2,   Handle)   do   
          OnTerminate   :=   ThreadDone;   
      with   TQueryThread.Create(DSPRemote_3,   3,   Handle)   do   
          OnTerminate   :=   ThreadDone;   
      with   TQueryThread.Create(DSPRemote_4,   4,   Handle)   do
          OnTerminate   :=   ThreadDone;
end;procedure TMainForm.FormCreate(Sender: TObject);
begin
     FQueue   :=   TQueue.Create;
end;
procedure   TMainForm.WMQueryDone(var   Msg:   TMessage);   
begin   
    case   Msg.WParam   of
    0:  begin
        CDSLocal.Open;// FQueue.Push(CDSLocal);
        end;
    1:   FQueue.Push(CDSRemote_1);   
    2:   FQueue.Push(CDSRemote_2);
    3:   FQueue.Push(CDSRemote_3);   
    4:   FQueue.Push(CDSRemote_4);   
    end;   
end;
    
procedure   TMainForm.ThreadDone(Sender:   TObject);
begin   
    if   CDSLocal.Active   then   
    begin
        while   FQueue.AtLeast(1)   do
        begin   
            with   TClientDataSet(FQueue.Pop)   do   
            begin   
                Open;
                CDSLocal.AppendData(Data,   True);
            end;
        end;
    end;
    Dec(FThreadsRunning);
    if   FThreadsRunning   =   0   then
        BRefresh.Enabled   :=   True;   
end;procedure TMainForm.BtnOneThreadClick(Sender: TObject);
var
  timebegin,timeend : Tdatetime;
begin  DBGridLocal.DataSource := DS_OneThread;
  timebegin := Now;
  with ADODataSet1 do
  begin
     Close;
     CommandText  := ' select top 100000 * from VW_test  ';
     Open;
  end;
  timeend := Now;
  Edit1.Text := DateTimeToStr(timeend-timebegin);
end;procedure TMainForm.BtnrecordClick(Sender: TObject);
begin
  if DBGridLocal.DataSource.DataSet.RecordCount > 0 then
    Edit_Record.Text := IntToStr(DBGridLocal.DataSource.DataSet.RecordCount);
end;end.
================

解决方案 »

  1.   

    问题在:
        procedure  TMainForm.WMQueryDone(var  Msg:  TMessage);  和procedure      TMainForm.ThreadDone(Sender:  TObject); 之间没有同步。也就是说当你1个线程事做完后在执行ThreadDone地时候,你的FQueue.AtLeast(1)里面不一定有内容。
    PostMessage(FHandle,  WM_QueryDone,  FTag,  0);  //发送查询结束消息  
    你把这里改为SendMessage试试。
    或者你用1个Event把发送的消息和ThreadDone过程同步下。
      

  2.   

    你现在处理的方式:
    当你线程给主Form发送WM_QueryDone消息以后马上返回接着做ThreadDone。
    这个时候你想一想,不一定在你做ThreadDone地时候主Form就一定接收到你先前发送的WM_QueryDone消息。如果这种情况发生,就会出现你说的掉1个线程的数据。而且掉地应该是最后1个发送消息的线程。
      

  3.   

    看了下线程,感觉没必要用线程,你这个都是open操作procedure  TQueryThread.Execute; 
    begin 
        CoInitialize(nil); 
        try 
            FQuery.Open; 
            PostMessage(FHandle,  WM_QueryDone,  FTag,  0);  //发送查询结束消息 
        finally 
            CoUnInitialize; 
        end; 
    end; 
    ==》procedure  TQueryThread.Execute; 
    begin 
        CoInitialize(nil); 
        try 
          Synchronize(mymethod1);  
        finally 
            CoUnInitialize; 
        end; 
    end; procedure TQueryThread.mymethod1;
    begin
      FQuery.Open;
      PostMessage(FHandle,  WM_QueryDone,  FTag,  0);  //发送查询结束消息
    end;
      

  4.   

    事实的确是这样的,每个线程查询的数据的确都查到了。
    但AppendDate出问题了,掉的也总是靠后的查询。
      

  5.   


    ==============================
    改为SendMessage后,貌似正常了,狂试了几十把,全部正常。
    提前小小的庆祝下,
    我再继续试试你的其他方法。
    无比感谢。
    无比敬仰中。
      

  6.   

    我这边软件使用的是三层结构,大数据量查询的时候,效率不理想,并且“死机”,白屏。我想实现的功能是将大数据量的查询分解为若干小的查询。用多线程进行。最后汇总为一个数据集。目前基本功能已经实现,但有一点不足:全部数据装载到ClentdataSet后,才显示数据。
    这个过程大概有几秒(数据量大的时候)。
    既然已经是多线程查询了,能否边查询,边显示?
    顶者有分啊
    晚上结贴。