多线程查询数据库,偶尔会丢掉一个线程。但大部分时候是正常。苦恼中。

lzg827 2010-01-06 11:02:53
如题。使用多个线程查询数据,然后汇总为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;

implementation

uses
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;

interface

uses
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.
================
...全文
411 10 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
dd_zhouqian 2010-01-07
  • 打赏
  • 举报
回复
up 太迟了,不看了
dd_zhouqian 2010-01-07
  • 打赏
  • 举报
回复
看了下线程,感觉没必要用线程,你这个都是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;


kfcoffe 2010-01-07
  • 打赏
  • 举报
回复
希望对你有帮助
kfcoffe 2010-01-07
  • 打赏
  • 举报
回复
你现在处理的方式:
当你线程给主Form发送WM_QueryDone消息以后马上返回接着做ThreadDone。
这个时候你想一想,不一定在你做ThreadDone地时候主Form就一定接收到你先前发送的WM_QueryDone消息。如果这种情况发生,就会出现你说的掉1个线程的数据。而且掉地应该是最后1个发送消息的线程。
kfcoffe 2010-01-07
  • 打赏
  • 举报
回复
问题在:
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过程同步下。




taste品味 2010-01-07
  • 打赏
  • 举报
回复
恰好路过....
lzg827 2010-01-07
  • 打赏
  • 举报
回复
楼上的兄弟,帮帮忙
lzg827 2010-01-07
  • 打赏
  • 举报
回复
我这边软件使用的是三层结构,大数据量查询的时候,效率不理想,并且“死机”,白屏。

我想实现的功能是将大数据量的查询分解为若干小的查询。用多线程进行。最后汇总为一个数据集。

目前基本功能已经实现,但有一点不足:全部数据装载到ClentdataSet后,才显示数据。
这个过程大概有几秒(数据量大的时候)。
既然已经是多线程查询了,能否边查询,边显示?
顶者有分啊
晚上结贴。
lzg827 2010-01-07
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 kfcoffe 的回复:]
问题在:
    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过程同步下

[/Quote]

==============================
改为SendMessage后,貌似正常了,狂试了几十把,全部正常。
提前小小的庆祝下,
我再继续试试你的其他方法。
无比感谢。
无比敬仰中。。。。
lzg827 2010-01-07
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 kfcoffe 的回复:]
你现在处理的方式:
当你线程给主Form发送WM_QueryDone消息以后马上返回接着做ThreadDone。
这个时候你想一想,不一定在你做ThreadDone地时候主Form就一定接收到你先前发送的WM_QueryDone消息。如果这种情况发生,就会出现你说的掉1个线程的数据。而且掉地应该是最后1个发送消息的线程。

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

1,594

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 网络通信/分布式开发
社区管理员
  • 网络通信/分布式开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧