在线程执行数据库查询强行终止查询并释放线程?(100分)

  • 主题发起人 主题发起人 lxggc
  • 开始时间 开始时间
to barton:
我照你上述的代码试了,当通不过,你的那句是不是应该是waitformultipleobjects()?
我把我的源代码贴出来,希望你帮我看一下:
//主窗体:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,unit2, StdCtrls,Syncobjs;
type
TQueryThread = class(TThread)
protected
procedure Execute;
override;
end;

type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject;
var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
GlobalEvent: THandle;
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin

GlobalEvent := OpenEvent(EVENT_MODIFY_STATE + SYNCHRONIZE, False, 'Client_Table');
end;

procedure TForm1.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
CloseHandle(GlobalEvent);
end;

{ TQueryThread }
procedure TQueryThread.Execute;
var
WaitTime: DWord;
begin
inherited;
FreeOnTerminate := True;
while not Terminateddo
begin
WaitTime := 3000;
//超时设为3秒
case waitformultipleobjects(1,GlobalEvent,False,WaitTime,QS_ALLEVENTS) of
WAIT_OBJECT_0: //这地方你原来是WaitForSingleObjects(),但编译时报错
该函数找不到,并且参数过多;改成这个后,编译报错GlobalEvent找不到,
并且参数过多,不知何故?
begin
ResetEvent(GlobalEvent);
if unit2.DataModule2.ClientDataSet1.Active then
//现在数据库表已经打开,爱干点啥就干点啥吧!
end;
WAIT_TIMEOUT:
begin
//在预期时间内未打开表,爱干点啥就干点啥吧!
Terminate;//自己终止自己
end;
end;
end;

end;

procedure TForm1.Button1Click(Sender: TObject);
begin
TQueryThread.Create(False);
unit2.DataModule2.ClientDataSet1.Open;
end;

end.
//Datamodul窗体:
unit Unit2;
interface
uses
SysUtils, Classes, DB, DBClient, MConnect, SConnect;
type
TDataModule2 = class(TDataModule)
SocketConnection1: TSocketConnection;
ClientDataSet1: TClientDataSet;
procedure ClientDataSet1AfterOpen(DataSet: TDataSet);
private
{ Private declarations }
public
{ Public declarations }
end;

var
DataModule2: TDataModule2;
implementation
uses Unit1;
{$R *.dfm}
procedure TDataModule2.ClientDataSet1AfterOpen(DataSet: TDataSet);
begin
SetEvent(unit1.Form1.GlobalEvent);
end;

end.

 
是..我贴的不是完整代码,我现在给你写一个完整的。OK?
 
好啊。太感谢了!!
 
完全按我说的操作,然后自己再领会,好吗?
1.建立一个新项目,一个窗体。窗体中一个Panel一个Memo,Panel上加两个按钮。
2.在主窗体单元的
implementation
{$R *.dfm}
下加入以下代码:
var
GlobalEvent: THandle;
type
TAsynEventThread = class(TThread)
private
FParentHandle: HWND;
procedure SendMsg(const Msg: string);
public
constructor Create(AParentHandle: HWND);
procedure Execute;
override;
end;

// T A s y n T h r e a d T h r e a d
constructor TAsynEventThread.Create(AParentHandle: HWND);
begin
FParentHandle := AParentHandle;
inherited Create(False);
end;

procedure TAsynEventThread.SendMsg(const Msg: string);
var
I: Integer;
begin
for I := 1 to Length(Msg)do
PostMessage(FParentHandle, WM_CHAR, Ord(Msg), 0);
PostMessage(FParentHandle, WM_CHAR, VK_RETURN, 0);
//回车
end;

procedure TAsynEventThread.Execute;
var
WaitTime: DWord;
begin
SendMsg('Thread Start.....');
FreeOnTerminate := True;
try
ResetEvent(GlobalEvent);
while not Terminateddo
begin
WaitTime := 3000;
//超时设为3秒
case WaitForSingleObject(GlobalEvent, WaitTime) of
WAIT_OBJECT_0:
begin
ResetEvent(GlobalEvent);
SendMsg('Event Occur!!!!');
Terminate;
end;
WAIT_TIMEOUT:
begin
SendMsg('Timeout!!!!');
Terminate;
end;
end;
end;
finally
SendMsg('Thread safely terminated.');
end;
end;

3.加入窗体事件句柄:
窗体OnCreate和OnDestroy:
procedure TForm1.FormCreate(Sender: TObject);
var
ASecurity_Attributes: SECURITY_ATTRIBUTES;
begin
ASecurity_Attributes.nLength := SizeOf(SECURITY_ATTRIBUTES);
ASecurity_Attributes.lpSecurityDescriptor := nil;
ASecurity_Attributes.bInheritHandle := False;
GlobalEvent := CreateEvent(@ASecurity_Attributes, True, False, 'Client_Table');
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
CloseHandle(GlobalEvent);
end;

按钮一OnClick:
procedure TForm1.Button1Click(Sender: TObject);
begin
TAsynEventThread.Create(Memo1.Handle);
end;

按钮二OnClick:
procedure TForm1.Button2Click(Sender: TObject);
begin
SetEvent(GlobalEvent);
end;

4.保存,运行
先按下按钮一,等待数秒,线程自己超时终止;
再按下按钮一,立刻按按钮二,线程捕获到事件终止。
看看CPU占用,基本上为0%。这点如果将超时加大可以说明。
 
Main:
Timer1OnTime: // Interval = 3000;
PostThreadMessage(Thread.ThreadID, WM_USER, 0, 0);
MyThread.Execute;
var
msg: TMsg;
begin
while not Terminateddo
begin
if GetMessage(msg, 0, 0, 0) then
case msg.message of
WM_USER: ;
// say hello
WM_CLOSE: ;
// oh, break;
....
end;
end;
end;

这样也简单点。
PeekMessage使用前需要PM_NOREMOVE一下,不然无法取得消息。
Event的方法需要再消耗一个Handle资源,不过,确是很好用。
而对于楼主的这个问题,俺觉得最好的方法去在Query Analyzer运行存储过程,并运行SQL的
Profiler监视程序,然后运行后,中止你的存储过程,然后看Profilter发生了什么事,或者
是用ADO的异步运行方法,不过这种方法俺也没怎么试过,你在Demos/ADO/ADOTest中看看它是
怎么进行AysncExecute的,可以参考。
TerminatedThread是强制性的,对于Application的损害是不可预见的,俺是从来没运行过
这东东,也不知效果如何,呵,天知道。
另,这种多线程的程序,到Delphi的Source中你找找Thread关键字,有N多这样的例子,写
的很不错。。。
 
copy_paste:
看得出你很明白线程中的消息机制,很好。只可惜你的代码同样会占用100%CPU。因为你没
有等待机制。你的线程总是读消息,没有消息返回,再读,再没有消息返回,再读...
其实与死循环没有什么区别。
补充一点:线程中昼不要用GetMessage,而用PeekMessage。
 
OK,我明白。感谢 barton 和 beta 。
 
GetMessage和PeekMessage区别其实就是一个阻塞和非阻塞,
你的while ... PeekMessage之所以会CPU 100%,就是因为它是非阻塞,PeekMessage应该和
WaitMessage组合使用,就像我们平常的Application,它使用的就是PeekMessage,那我们的
程序什么时候会CPU 100%?
GetMessage就是阻塞式的,线程消息队列有消息才返回,和WaitFor的原理相同了。至于会不会
CPU 100%,那要看线程响应消息时所作的处理,而不是代码结构本身了。
 
to barton:
我完全按照你说的做了,运行是成功的。可是跟SQL一打交道后就有问题了。因为我的终极
目的是要实现打开数据库中的表,因此,我一定要有个事件来打开表,在这基础上设置GlobalEvent。
我试了两种方法:
其中unit2就是个datamodule,上面放一个socketconnection和clientdataset
1。把button2Click中改成:
procedure TForm1.Button2Click(Sender: TObject);
begin
unit2.DataModule2.SocketConnection1.Connected:=true;
unit2.DataModule2.ClientDataSet1.Open;
SetEvent(GlobalEvent);
end;
2。在线程TAsynEventThread创建时打开:
procedure TForm1.Button1Click(Sender: TObject);
begin
TAsynEventThread.Create(Memo1.Handle);
unit2.DataModule2.SocketConnection1.Connected:=true;
unit2.DataModule2.ClientDataSet1.Open;
SetEvent(GlobalEvent);
end;
两种试下来都不行,我调试时,将SQL的应用服务器关掉,然后运行程序,只要一连接数据库,
主界面就死在那,一定要等到连接出错返回报告显示后,才在memo1上显示:'Timeout!!!!'
和'Thread safely terminated.'
 
你上面不管那种方法都是在主线程进行SQL数据库连接,当然死在那里了。异常后根本就没有
运行SetEvent操作,跟着工作线程也就因Timeout而结束,但发过来的消息因主线程连接SQL
而阻塞,直接返回才响应。
你应该将Connection.Open放到线程,或者一开始就连接上去,线程做的事是事情都准备的差
不多了,然后做的,它做的是繁复的工作,而不是Connected,Connection中好像已经是一个
线程了,不太记得了。应该是的。
 
谁在问问题啊??
 
楼主设线程的目的只是如果在3秒内没有打开数据库则提前报错。如果数据库在3秒内打开,
设置事件为发信号状态,线程结束。如果3秒内未打开,数据库也未报错,则线程结束,提
前通知主线程。本线程的目标已经完成。
实话说,做这个线程没有多在意义,我从来就不这么用的。我都是自己在线程中调用API打
开数据库,我从不用Dataset/ClientDataset的。
 
API打开数据库??
barton,用什么打开,写两行学习学习。:)
不会是像VC那样:SQLConnect.... SQL....
俺晕...
 
to barton:
你是用API打开数据库的?能设置延迟时间以达到在3秒内未打开数据库则报错吗?我
开始时想到用API,可是查不到相关资料,所以才想到用多线程。小弟是新手,还请多多
指教!
还有你刚刚说的目标已完成,可是我刚刚试了通不过,不知何故,原因见我上面的帖子
 
不是呀。你们可能有误解。
我用SQLServer时,从前用DB-Library,现在用ADO或者DMO,都有例程的呀,我直接按原生
的接口到我的对象中,不经过TField/TDataset的,比较好控制。
Access文件型一直用原生的DAO。好使。
Interbase则更方便了。本身就有接口的。
主要是游标控制罢了。
 
to barton:
原来是这样的呀。不过我很想知道我上面贴的问题如何才能解决。盼告之!!!
 
我用线程同步也一般不用event,只用semaphore和线程本身。
1.主线程休眠等待特定线程开始执行:
在TThread构造方法中建立一个Semaphore,进入Execute过程后,首先ReleaseSemaphore。
主线程运行完TThread.Create后,WaitForSingleObject(Thread.Semaphore, INFINITE)
2.主线程休眠等待线程结束:
主线程中Thread.Terminate后,WaitForSingleObject(Thread.Handle, 180000)。
(最多等待三分钟)
3.线程休眠等待主线程指令:
一律用消息。一般不用系统预定义的消息,防止哪个窗口广播消息,产生无法预料的效果。
主线程端:
PostThreadMessage(Thread.ThreadID, WM_XXX, WParam, LParam);
从线程端:
前面讲了很多,copy_paste补充得非常好。
 
to barton:
我刚刚试了一下,创立另一个线程Topnethread,源码如下:
type
TOpneThread = class(TThread)
private
public
procedure Execute;
override;
end;

procedure TOpneThread.Execute;
begin
inherited;
while not Terminateddo
begin
unit2.DataModule2.SocketConnection1.Connected:=True;
unit2.DataModule2.ClientDataSet1.Open;
SetEvent(GlobalEvent);
end;
end;

按钮一的事件改为:
procedure TForm1.Button1Click(Sender: TObject);
begin
TAsynEventThread.Create(Memo1.Handle);
TOpneThread.Create(false);
end;

然后我想在线程TAsynEventThread.Execute事件中加入代码,当超时时将线程2kill掉,
但我不知该如何写才能使线程2尽快被杀掉。还望指教!
 
不可能的。
unit2.DataModule2.SocketConnection1.Connected:=True;
unit2.DataModule2.ClientDataSet1.Open;
这两行任何线程都无法打断。
 
那用API打开SQL SERVER,可以达到我要的功能吗?
 
后退
顶部