(底分100)大侠看过来:用线程执行数据库查询,挂起(中断)后再次执行查询,如何解决报的这个错??(100分)

孟雯

Unregistered / Unconfirmed
GUEST, unregistred user!
我在主程序中用线程执行数据库的查询操作,以便时间长的查询可由用户中断。
所用 Query 拥有自己的 Session、Database 、DataSource和DbGrid;
不同的是:每次执行的查询语句。
另外由于主程序的不同地方调用线程,所以在thread的exeute 事件加了不同的
synchronize(method)代码,来访问VCL资源。我想这个应该不成问题。
我的问题是:
每次查询中断以后,我再次执行新的查询,执行TDBQueryThread.Execute事件会报错
“can't perform this operation on an open dataset”
代码:
TDBQueryThread = class(TThread)
private
{ Private declarations }
FQry_TmT : TQuery;
FDS_TmT : TDataSource;
FDB_TmT : TDataBase;
FSQl_TmT : string;
FRecNum : integer;
F_Flag : Integer;
FQueryException : Exception;
procedure HooKUpUI;
procedure QueryError;
procedure AddSGTM;
Function FindInSG(rn:string;ic:string):integer;
procedure GiveRecNum;
procedure PDoAuto;
procedure PDoPartAuto;
protected
procedure Execute;
override;
public
constructor Create(Qry_TmT: TQuery;
Ds_TmT: TDataSource;Db_TmT:TDataBase;SQL_TmT:String;Flag :Integer);virtual;
end;

var
Main_Form: TMain_Form;
MyThread : TDBQueryThread;
constructor TDBQueryThread.Create(Qry_TmT: TQuery;
Ds_TmT: TDataSource;Db_TmT:TDataBase;SQL_TmT:String;Flag :Integer);
begin
inherited Create(True);
FQry_TmT := Qry_TmT;
FDS_TmT := Ds_TmT;
FDB_TmT := Db_TmT;
FSQl_TmT := Sql_TmT;
F_Flag := Flag;
FreeOnTerminate := True;
Resume;
end;

procedure TDBQueryThread.Execute;
begin
try
with FQry_TmTdo
begin
close;
sql.Clear;*** 中断后执行到此处报错“can't perform this operation on an open dataset”
sql.Add(FSql_TmT);

prepare;
open;
first;
while not eofdo
begin
Inc(FRecNum);
Next;
end;
First;
synchronize(GiveRecNum);
Case F_Flag of
0: begin
synchronize(AddSGTM);
synchronize(PDoAuto);
end;
1: begin
synchronize(HookUpUI);
synchronize(PDoPartAuto);
end;
...
end;
end;
except
FQueryException:=ExceptObject as Exception;
Synchronize(QueryError);
end;
end;

调用: MyThread := TDBQueryThread.Create(Dm_Form.QryTm,Dm_Form.DS_Tm,Dm_Form.DBS_Qry,S_Sql,0);
挂起: MyThread.Suspend ;
MyThread.Free;
各位大侠可看出问题来了吗???
期盼援手。。
100分奉上,不够再加。
多谢!
 
S

snake

Unregistered / Unconfirmed
GUEST, unregistred user!
不用看,这个肯定不行,线程中断后查询并没有中断,所以重新打开的时候会提示
错误。所以说线程并不能中断查询,只能从底层的odbc api用异步查询中断, 但和
具体的数据库类型也有很大关系,如果是用sql server的话用odbc express就可以
简单实现,其它的数据库我还没找到那个控件可以实现呢。用odbc编程又很烦。所以
真是很烦。
 
A

amo

Unregistered / Unconfirmed
GUEST, unregistred user!

thread的
OnTerminate
事件中加入释放资源的语句
 
S

snake

Unregistered / Unconfirmed
GUEST, unregistred user!
query在执行状态下释放会报错的。
 

孟雯

Unregistered / Unconfirmed
GUEST, unregistred user!
再问一个低级的问题,我能在 TDBQueryThread.Execute 事件中把 线程终止吗??
我这样做:
rocedure TDBQueryThread.Execute;
begin
try
with FQry_TmTdo
begin
close;
sql.Clear;
sql.Add(FSql_TmT);
prepare;
open;
if Terminated then
*** 此处终止
Terminate;
*** 终止
First;
。。。。。。。
根本无济于事,哪位大侠再给指点一下,最好是连资源释放,
(要代码,有效的),对了给分。

 

孟雯

Unregistered / Unconfirmed
GUEST, unregistred user!
PS :我用的是 N T + Delphi 4 + Oracle.
 
O

only you

Unregistered / Unconfirmed
GUEST, unregistred user!
1.该打开的数据库没有打开。
2.不行,除非强行终止,但资源并不释放。
 
O

only you

Unregistered / Unconfirmed
GUEST, unregistred user!
强行终止一个线程用:
TerminateThread(MyThread.Handle,0);
 
S

snake

Unregistered / Unconfirmed
GUEST, unregistred user!
try
with FQry_TmTdo
begin
close;
sql.Clear;
sql.Add(FSql_TmT);
prepare;
open;
////到这里就停住了,除非query执行完毕,否则不会检测Terminated
if Terminated then
*** 此处终止 //所以这句没实质意义
Terminate;
*** 终止
First;
。。。。。。。
 
K

Kent

Unregistered / Unconfirmed
GUEST, unregistred user!
其实就算你在客户端上把查询线程杀掉了,但是数据库一端并不一定就把连接马上释放掉,
可能残留下来,数据库有专门负责检查这种残留进程的进程,负责把它们杀掉,
以你的要求,你就要在一个线程里面执行查询,另一个线程监视用户的动作,用户按
停止按钮就由这个线程去把查询线程杀死....
 

孟雯

Unregistered / Unconfirmed
GUEST, unregistred user!
1):only you
我已经加入以下代码,打开数据库和终止进程,但令我伤心的是:错误依旧:
“can't perform this operation on an open dataset” 。!!
。。。
if Not FDB_TmT.Connected then
//***open database
FDB_TmT.Connected := True;

with FQry_TmTdo
begin
close;
sql.Clear;
sql.Add(FSql_TmT);
...
又: TerminateThread(MyThread.Handle,0);

2):snake
您分析的很有道理啊,我是多写了几句废代码,正确的是什么呢??
3) :Kent ,我该如何在客户端(应用程序)上调用数据库的手段杀掉 进程 呢 ?
是这个意思吗??
 
K

Kent

Unregistered / Unconfirmed
GUEST, unregistred user!
你在客户端上不能调用数据库的手段杀掉查询进程 ,因为这是 oracle 后台进程自动做的
,在界面端是控制不了的,也没必要控制.
要想做成任意终止查询操作而且不占用多余的资源我想不太可能,除非DBMS是你自己写的,
但是就算是自己写的也不容易,想想看,如果你写一个程序去读一个很大的文件,读到一半
时,叫你马上停下来怎么办哪?要么在外部强行杀死,要么再读数据块时处理消息,
但是这样做效率就会降低,而象oracle数据库这样复杂的系统为了处理大量业务,我想
不会在读数据的时候处理消息,所以它使用外部强行杀死的办法。
所以我觉得你能做的就是在客户端把查询线程杀掉,其余的善后工作交给oracle处理了,
 

孟雯

Unregistered / Unconfirmed
GUEST, unregistred user!
>> :Kent
是啊,我也不想利用应用程序做数据库做的事(比如终止查询进程),
我只是想利用 线程 使用户可以决定是否等待此次查询做完,不想的话,就把查询进程挂起,
重要的是,可以再次执行新的查询。
现在,利用线程正常查询是没有问题的,一次执行完后,可以再次执行新的查询,但,
如果前一次中断查询,再次执行就会报错,我怀疑是自己的中断(挂起)方法有问题。
所以,请各位高手们着重帮我看一下如何中断,再次查询,资源的正确释放和 create
的问题。

诸位以为呢??
 
K

Kent

Unregistered / Unconfirmed
GUEST, unregistred user!
你想挂起后再继续执行查询,我觉得数据库本身就不支持,因为它不会白白的占用资源,
因为你查到一半时有一个很大的结果集存在池里,这时候你查询挂起,那么为了能让你
继续查询,就必须保留结果集和其它一些信息,这样的话,如果你一个小时也不继续查询,
那你就独占资源1小时,这对数据库来说是不能忍受的。
所以我说,没有办法...
 
O

only you

Unregistered / Unconfirmed
GUEST, unregistred user!
这就对了,不是告诉你线程的资源是不释放的,而且不在极端情况下是不能使用它的
因为帮助说它是个极端危险的函数。其实如果把线程暂时挂起如何!
如MyThread.suspend;
 
S

snake

Unregistered / Unconfirmed
GUEST, unregistred user!
suspend这个东西没用,他只是对程序挂起,并没对query挂起,所以也是没有。
如果有办法实现相delphi里调试的reset功能就好了。
 

孟雯

Unregistered / Unconfirmed
GUEST, unregistred user!
真的没有人知道吗?
我已经试了所能想到的所有方法。
让人伤脑筋。。。
 
S

snake

Unregistered / Unconfirmed
GUEST, unregistred user!
这个问题问了不少次了,据我所知最后还是没有答案。
 
J

jiangyiquan

Unregistered / Unconfirmed
GUEST, unregistred user!
我的机器这2日连不上大富翁,上网没问题,如何把我的答案通知你?
发email如何?
------
关键是你对线程的概念、同步的概念和Delphi的TThread对象了解得不够。
 
K

kens

Unregistered / Unconfirmed
GUEST, unregistred user!
其实你的问题很简单,你的线程中根本没有 OnTerminate 事件,该事件在线程被中断或
线程执行完毕以后调用,只要你在该事件中断开关闭已经打开的 Query 或 Table ,然后
再断开数据库的连接和释放资源,下次线程再运行的时候就不会出错了。
 
顶部