多线程查询数据库,偶尔会丢掉一个线程。但大部分时候是正常。苦恼中。 (问题解决,有待优化,欢迎发表意见,顶者有分,晚上结贴)(100)

  • 主题发起人 主题发起人 完美动物
  • 开始时间 开始时间

完美动物

Unregistered / Unconfirmed
GUEST, unregistred user!
如题。使用多个线程查询数据,然后汇总为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 ADODataSet1do
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.
================PostMessage改为SendMessage 即可正常AppendDate。因为PostMessage发出去就不管了,此时Form可能没收到消息,于是FQueue可能没有数据。各位有任何看法继续发表,目前仅仅实现基本功能而已。如:smlabc所说的,我依然觉得这是一个蹩脚的实现方式。顶着有分啊。晚上结贴。
 
怎么可能,有时候丢失,有时候正常。你开了多少个线程啊
 
一共就5个线程而已。确实是很奇怪的问题。我已经仔细检查了数据集,在少数情况(大概十几次中出现1次)有1个线程的数据集丢失。
 
你的数据集读写 没有问题吧,是线程出的错么?
 
//这段代码应该加入临界区uses SyncObjs;
//这个单元把代码修改为:FCdsDataSetLock: TCriticalSection;
//这个记得创建啊procedure TMainForm.ThreadDone(Sender: TObject);
begin
FCdsDataSetLock.Enter;
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;
FCdsDataSetLock.Leave;
end;
 
线程,不是这么用的.线程是为了提高效率的,做戏也要做全套.如果我设计这个结构,传入线程的就只会是一个连接字符串,而不是TDataSetProvider.创建时的参数传入,别改他的构造函数,挂起线程后赋值其属性,再运行.数据处理都放线程里做,主线程只需要显示.别用数据感知控件,这对你有好处的.另有小问题:线程中的变量FQuery是不需要释放的?
 
To:kk2000非常感谢你的关注。但按上面修改后依然不行,并且似乎出现的更频繁了。。。。我已经检查过每个线程的数据集,线程查询的结果并没有丢失。问题的确出在AppendDate这个过程中。求大侠再次出手,我觉得离解决已经是一步之遥了。
 
To smlabc: 你好, 感谢你的回复。 个人认为你说的的确很有道理。比如:感知控件的看法。 只是第一次做多线程的东西,实在没有方向感,搞了几天了,还有点晕乎乎的。上面的代码也是从网上找的,先改改,练习下,顺便实现功能。 其实我想实现的仅是将一个大数据量的查询分解为几个小查询同时进行,然后合并为一个数据集。 还请提供直接的例子,刚接手这些东西,实在很菜,说多了我也不明白。 当然,本来也就很菜。。。。。囧
 
PostMessage改为SendMessage 即可正常AppendDate。因为PostMessage发出去就不管了,此时Form可能没收到消息,于是FQueue可能没有数据。各位有任何看法继续发表,目前仅仅实现基本功能而已。如:smlabc所说的,我依然觉得这是一个蹩脚的实现方式。顶着有分啊。晚上结贴。
 
或许那个查询并没有长到不能忍受的地步,如果真是那样就不需要用线程了。
 
http://www.delphibbs.com/delphibbs/dispq.asp?lid=3971379我2年前的代码,你可以参考下
 
谢谢smlabc的代码。收下学习了。o(∩_∩)o...哈哈谢谢各位捧场。^_^
 
后退
顶部