看看我的多线程ADO数据库查询代码(100分)

  • 主题发起人 主题发起人 grasshoper
  • 开始时间 开始时间
G

grasshoper

Unregistered / Unconfirmed
GUEST, unregistred user!
1,我是想做一个多线程的ADO的数据库查询程序,然后点了查询按钮后不影响程序的其他操作。可这个程序一点按钮开始查询线程就报错,什么runtime error 216之类的。希望能提供个无错的解决方法~~~
2,我想让线程查询完成后触发Onterminate来执行ondone事件,将查询结果写入listview1。但我在按钮事件里添加不了t1.OnTerminate:=Form1.ondone;
把ondone事件写进线程类的事件里然后想在create时定义Onterminate事件也是不行。
小弟初学delphi,大家多指教~~~~这是我的第一贴
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, ADODB, StdCtrls, ComCtrls, ExtCtrls;
type
TForm1 = class(TForm)
ADOConnection1: TADOConnection;
ADOQuery1: TADOQuery;
Button1: TButton;
ListView1: TListView;
Memo1: TMemo;
Timer1: TTimer;
procedure Button1Click(Sender: TObject);
procedure writetolv(listview:TListView;ado:TADOQuery);
procedure Timer1Timer(Sender: TObject);
procedure ondone;
private
public
{ Public declarations }
end;

type
TsqlThread = class(TThread)
private
adoquery:TADOQuery;
adocnn:TADOConnection;
sqlex:string;
lv:TListView;
protected
procedure Execute;override;
public
constructor Create(sql2:string;adoquery2:TADOQuery);
destructor Destroy;override;
end;

var
Form1: TForm1;
t1:TsqlThread;
implementation
uses ComObj;
{$R *.dfm}
constructor TsqlThread.Create(sql2:string;adoquery2:TADOQuery);
begin
adocnn:=TADOConnection.Create(nil);
adocnn.KeepConnection:=true;
adocnn.LoginPrompt:=false;
adocnn.ConnectionString:='Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=pmdb;Data Source=127.0.0.1';
adoquery:=adoquery2;
sqlex:=sql2;
inherited Create(false);
end;

destructor TsqlThread.Destroy;
begin

inherited Destroy;
end;

procedure TsqlThread.Execute;
begin
adoquery.Connection:=adocnn;
if adoquery.Active then
begin
try
adoquery.Close;
adoquery.Active:=false;
adoquery.SQL.Clear;
adoquery.SQL.Add(sqlex);
adoquery.Open;
adoquery.Active:=false;
except
on e:Exceptiondo
begin
ShowMessage(e.Message);
exit;
end;
end;
end else
exit;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
sql:string;
begin
sql:='select * from pmdb.dbo.all_zone';
t1.Create(sql,ADOQuery1);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
writetolv(ListView1,ADOQuery1);
Timer1.Enabled:=false;
end;

procedure TForm1.writetolv(listview:TListView;ado:TADOQuery);
var
i:integer;
begin
try
//生成列名
for i := 0 to ado.Fields.Count-1do
with listview.Columns.Adddo
begin
Caption := ado.Fields.DisplayLabel;
end;

//将记录写入行
ado.First;
while not ado.Eofdo
begin
with ListView.Items.Adddo
begin
Caption:=ado.Fields[0].AsString;
//第一列
for i := 1 to ado.Fields.Count - 1do
//第2列到最后一列
SubItems.Add(trim(ado.Fields.AsString));
end;
ado.Next;
end;
ado.Close;
ado.Active:=false;
except
ShowMessage('写入失败');
exit;
end;
end;

procedure TForm1.ondone;
begin
Timer1.Enabled:=true;
end;

end.
 
你的第一帖啊?都把dfw帖晕了,把问题说清楚有利于大家帮你,不要大段大段帖代码.
 
问题说清楚了的啊 在代码前面 分了2点的 大侠们帮忙看看吧~~~~~~~~
 
第一个问题:
var
Form1: TForm1;
t1:TsqlThread;
procedure TForm1.Button1Click(Sender: TObject);
var
sql:string;
begin
sql:='select * from pmdb.dbo.all_zone';
t1.Create(sql,ADOQuery1);
////注意这里!
end;

看看上面的代码,犯了最容易犯的错误,拿对象去Create!
t1 := TsqlThread.Create(sql, ADOQuery1);
你的那个问题应该解决了
——————————————————————————
另外,你不应该将Query传入作为参数,应该在线程中,自己创建Query,否则的话会有线程同步问题,
作后可以将Query的ReocordSet返回给主线程。
第二个问题:
OnTerminate的事件声明为 TNotifyEvent = procedure(Sender: TObject) of object;
你的OnDone和这个类型不对,
建议申明成:procedure OnDone(Sender: TObject)
 
线程里面创建adoconnection之前最后要要进行对象初始化CoInitialize(nil);
完了要CoUninitialize;同时你要定义一个ondone函数有问题,你看看线程的Terminated函数就知道了。
 
xiammy,我按你说的改了代码。我声明了一个_recordset类的全局变量,然后把他传入到线程,在线程中创建query和connection进行查询后,把query的recordset复值给了那个全局变量。然后OnTerminate的问题也解决了,OnTerminate的事件直接就是writetolv(Sender: TObject),这个事件用来把那个_recordset类的全局变量的内容写到listview里。可我执行时还是报错了,提示是'尚未调用CoInitialize',不知道怎么办了。。。。[:(]
var //全局变量
Form1: TForm1;
dataset1:_Recordset;
t1:TsqlThread;
//定义线程类
type
TsqlThread = class(TThread)
private
adoquery:TADOQuery;
adocnn:TADOConnection;
sqlex:string;
lv:TListView;
resultset:_Recordset;
protected
procedure Execute;override;
public
constructor Create(sql2:string;indataset:_Recordset);
destructor Destroy;override;
end;

//线程的执行过程
constructor TsqlThread.Create(sql2:string;indataset:_Recordset);
begin
resultset:=indataset;
adocnn:=TADOConnection.Create(nil);
adocnn.KeepConnection:=true;
adocnn.LoginPrompt:=false;
adocnn.ConnectionString:='Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=pmdb;Data Source=127.0.0.1';
adoquery:=TADOQuery.Create(nil);
sqlex:=sql2;
inherited Create(false);
end;

destructor TsqlThread.Destroy;
begin

inherited Destroy;
end;

procedure TsqlThread.Execute;
begin
adoquery.Connection:=adocnn;
try
adoquery.Close;
adoquery.Active:=false;
adoquery.SQL.Clear;
adoquery.SQL.Add(sqlex);
adoquery.Open;
resultset:=adoquery.Recordset;
except
on e:Exceptiondo
begin
ShowMessage(e.Message);
exit;
end;
end;
end;
 
我就是提示没调用那个CoInitialize了 该加在哪里呢?我在线程create事件里按ctrl+space怎么没有这个过程的提示,只有一个CoInitializeEx?
 
我在uses里加上了ActiveX,然后在constructor TsqlThread.Create(sql2:string;indataset:_Recordset);事件里加入了CoInitialize(nil);
然后在destructor TsqlThread.Destroy;里加入了CoUninitialize;
但运行时还是提示尚未调用CoInitialize,晕死..............
 
adoquery2要先连接啊
adoquery:=adoquery2;
 
已经改成线程过程内自己创建adoquery了
现在的问题是总是提示尚未调用CoInitialize,我已经在create和destroy里加上了CoInitialize(nil)和CoUninitialize也没用,还是提示
 
uses shareMem
 
在Execute事件中添加
CoInitialize(nil)
try
finally
CoUninitialize
end
 
我已经解决尚未调用CoInitialize的问题了 我把CoInitialize和CoUninitialize加在Create和Destroy里的,同样没有问题 主要是因为查询的事件在线程事件里必须加上Synchronize,暂时还不明白为什么要加这个,不过现在程序已经无错了,可以同时进行2个查询的线程,然后再用2个写入线程将结果写入到listview。搞了一上午,终于有点收获了~~~呵呵
//***************************定义TsqlThread的具体过程************************************//
constructor TsqlThread.Create(sql2:string);
begin
CoInitialize(nil);
inherited Create(false);
adocnn:=TADOConnection.Create(nil);
adocnn.KeepConnection:=true;
adocnn.LoginPrompt:=false;
adocnn.ConnectionString:='Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=pmdb;Data Source=127.0.0.1';
adoquery:=TADOQuery.Create(nil);
adoquery.Connection:=adocnn;
sqlex:=sql2;
//sql2是传入的语句
end;

destructor TsqlThread.Destroy;
begin
adoquery.Free;
adocnn.Free;
CoUninitialize;
inherited Destroy;
end;

procedure TsqlThread.Execute;
begin
try
Synchronize(execsql);
//这里的Synchronize很关键
except
self.Terminate;
end;
end;

procedure TsqlThread.execsql;
begin
adoquery.Connection:=adocnn;
try
adoquery.Close;
adoquery.Active:=false;
adoquery.SQL.Clear;
adoquery.SQL.Add(sqlex);
adoquery.Open;
except
begin
self.Terminate;
exit;
end;
end;
end;
 
多人接受答案了
 
晕死 刚看了个关于Synchronize的贴子 原来Synchronize是把线程中的事件调到主线程去执行 完全违背了初衷了 我试了jacket84的方法了 加到Execute事件下去初始化COM 现在已经不用再用Synchronize了,完全由线程来执行ADO查询 这才是最好的效果 不好意思啊jacket84,给你分加少了些。。。。。下次再来帮忙一定给你高分
 
你把代码整理好,放到网上,让更多好,参与
 
其实Create事件跟destroy事件都是在主线程中运行,Execute才是线程独立运行的开始!
 
后退
顶部