帖子http://expert.csdn.net/Expert/topic/1492/1492778.xml解决了用线程查询的问题,但是新问题又来了,用什么方法中断正在查询的线程呢?
用Terminate方法是不行的,他仅仅设置了一个标志,如果直接delete查询的线程,也不行,报“Systemc Error:code 5 拒绝访问”错。
希望各位能探讨一下。

解决方案 »

  1.   

    应该不太可能吧,你看看此贴。
    http://www.delphibbs.com/delphibbs/dispq.asp?lid=1076676SQL运行中,你不可能去中断它,不过也没试过异步方式线程的中止,如果你作了线程的循环处理的话应该比较简单,但SQL命令就
      

  2.   

    想中止查询,只能用ADO的异步方式进行查询。即使不用额外的线程也没问题,它属于立即返回型调用,你需要主动询问查询是否结束,你可以在查询没有结束前中止查询。
      

  3.   

    耙子,你有异步方式的,比较完整的代码没?我看DEMO中的例子。没怎么看。有没?
      

  4.   

    procedure TForm1.ADOQuery1FetchProgress(DataSet: TCustomADODataSet;
      Progress, MaxProgress: Integer; var EventStatus: TEventStatus);
    beginend;你是说将EventStatus变成esCancel就行了???我以前试过,因为数据量比较大,试了一下做成异步,但后来发现Progess, MaxProgress都不太正确,而且好像那么对应的事件也不太确定,就弃之了。
      

  5.   

    请先看http://expert.csdn.net/Expert/topic/1492/1492778.xml帖子,我是客户端的查询线程,这里只能用TClientDataSet,好像它没有异步方式。
    我的程序框架是两个线程,主线程是界面,查询线程负责查询,查询完成后通过同步后在主界面显示结果。但是查询线程无法即时终止查询操作。
      

  6.   

    那不行了
    TClientDataSet的RemoteServer执行命令时,如果命令没返回,那不可能中止的线程的中止只是在循环中没有阻塞的情况下进行的,如果阻塞了俺不知道。
      

  7.   

    贴一个 :(班门弄斧莫怪:))立即终止线程查询 :
    ---------------------------------------
    unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls,UThTest, DB, ADODB;type
      TForm1 = class(TForm)
        Button1: TButton;
        Button2: TButton;
        Label1: TLabel;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
        procedure a(Sender : TObject) ;
      private
        { Private declarations }
      public
        { Public declarations }
      end;var
      Form1: TForm1;
      tt : TTest ;
      b : Boolean = True ;implementation
    {$R *.dfm}procedure TForm1.a(Sender : TObject) ;
    begin
      Caption := 'End Of The Thread In Gear ' ;
    end ;procedure TForm1.Button1Click(Sender: TObject);
    begin
      Caption := 'Form1' ;
      tt := TTest.Create(False);
      tt.OnTerminate := a ;
    end;procedure TForm1.Button2Click(Sender: TObject);
    begin
      b := False ;
      TerminateThread(TTest(tt).Handle,0);
      TTest(tt).Free ;
      Caption := 'end ' ;
    end;end.TThread :
    --------------
    unit UThTest;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, DB, ADODB ;type
      TTest = class(TThread)
      private
        { Private declarations }
        procedure p ;
      protected
        procedure Execute; override;
      end;implementation{ Important: Methods and properties of objects in VCL or CLX can only be used
      in a method called using Synchronize, for example,      Synchronize(UpdateCaption);  and UpdateCaption could look like,    procedure TTest.UpdateCaption;
        begin
          Form1.Caption := 'Updated in a thread';
        end; }{ TTest }uses Unit1 ;procedure TTest.Execute;
    begin
      { Place thread code here }
      Synchronize(p) ;
    end;procedure TTest.p ;
    var
      ADOQuery1 : TADOQuery ;
    begin
      ADOQuery1 := TADOQuery.Create(Nil) ;
      with ADOQuery1 do
      begin
        ConnectionString := 'Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initial Catalog=hdcims;Data Source=FRESHMAN ' ;
        Sql.Text := 'select * from zhang_cpkkc' ;
        Open ;
        First ;
        while Not Eof do
        begin
          if b then
          begin
            Application.ProcessMessages ;
            Sleep(100) ;
            Form1.Label1.Caption := FieldByName('cpkkc_jxname').AsString ;
            Next ;
          end
          Else
          begin
            Exit ;
          end ;
        end ;
      end ;
    end ;end.
      

  8.   

    ds->Open();
      Client Connect->SendCommand("select...")  ==> DCOM ==> Server invoke COM Obj interface ==> 
      run & waitfor SQL("select...") ==> Reponse client在中间那层run & waitfor SQL中,你就是应该使用异步来做,不过MIDAS技术根本不是这样做的,好像就没有这个支持,或许我没试过
      

  9.   

    大哥:) 你还好么 ?:) 我这样 用 TerminateThread + 一个全局 Boolean 可以及时终止的:)
      

  10.   

    质子:
      TThread.Execute中不要使用Synchronize进行主要的线程工作,因为它是将Synchronize中的(Method: TMethod)提交给主线程去做,所以这样,多线程就没有用了,而且如果是在线程中使用ADO or COM组件,应该配合使用CoInitialize & CoUnInitialize,真是该打你PP。。
      
      TerminateThread这个嘛,那个嘛,俺是没用过,也不知会有什么结果,因为我就不会去用它,所以就不去研究了:D:D:D
      

  11.   

    一般不用全局变量,全局对象能不用则不用。
    像你那个,将那变量去了。procedure TForm1.a(Sender : TObject) ;
    begin
      Caption := 'End Of The Thread In Gear ' ;
      tt := nil;
    end ;还有将tt 放在TForm1的private区域。
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      if Assigned(tt) then Exit;
      Caption := 'Form1' ;
      tt := TTest.Create(False);
      tt.OnTerminate := a ;
    end;procedure TForm1.Button2Click(Sender: TObject);
    begin
      if not Assigned(tt) then Exit;
      TerminateThread(TTest(tt).Handle,0);
      TTest(tt).Free;
      Caption := 'end ' ;
    end;
      

  12.   

    我写一个大致的例子,你们参考ADOConnect->ConnectOptions= coAsyncConnect; // 设置为异步查询;...
    ADOQuery->SQL........;
    ADOQuery->Open(); // 他会立即结束,不会等到查询完毕;
    // 循环等待,你可以干别的事儿,比如Application->ProcessMessages();
    while (ADOConnection->State = stExecuting || ADOConnection->State = stFetching) 
    {
      ....
      Application->ProcessMessages();
    }
    // 查询完毕或者可能被中止了
    ......
    你可以弄一个按钮,写上[中止]
    if (ADOConnection->State= stExecuting )
      ADOConnection->Cancel();代码我手写的,供你参考,不一定能通过bcb,但是意思是这样的。
      

  13.   

    楼主,不然这样,线程运行SQL的命令继续执行,但是当你想取消运行时,置线程某个变量为False,然后线程运行完成后,将判断这个值,然后决定是否将取出来的数据给主线程
    (用回调)觉得这样简单点吧。主线程和次线程的通讯方法比较多,用消息,事件最好不要线程中直接调用主线程的东东吧
      

  14.   

    耙子,Good,俺以前一直以为不行,原来是取ADOConnection的值,呵呵亲你一口
      

  15.   

    unit UThTest;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, DB, ADODB,ActiveX ;type
      TTest = class(TThread)
      private
        { Private declarations }
        s : String ;
        procedure p ;
      protected
        procedure Execute; override;
      end;implementation{ Important: Methods and properties of objects in VCL or CLX can only be used
      in a method called using Synchronize, for example,      Synchronize(UpdateCaption);  and UpdateCaption could look like,    procedure TTest.UpdateCaption;
        begin
          Form1.Caption := 'Updated in a thread';
        end; }{ TTest }uses Unit1 ;procedure TTest.Execute;
    var
      ADOQuery1 : TADOQuery ;
    begin
      { Place thread code here }
      CoInitialize(Nil) ;
      ADOQuery1 := TADOQuery.Create(Nil) ;
      with ADOQuery1 do
      begin
        ConnectionString := 'Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initial Catalog=hdcims;Data Source=FRESHMAN ' ;
        Sql.Text := 'select * from zhang_cpkkc' ;
        Open ;
        First ;
        while Not Eof do
        begin
          if b then
          begin
            Application.ProcessMessages ;
            Sleep(100) ;
            s := FieldByName('cpkkc_jxname').AsString ;
            Synchronize(p) ; // 这儿这个同步大哥改改:) ,我 Lock 不来了:)
            Next ;
          end
          Else
          begin
            Exit ;
          end ;
        end ;
      end ;
      CoUninitialize ;
      ADOQuery1.Free ;
    end;procedure TTest.p ;
    begin
      Form1.Label1.Caption := s ;
    end ;{  for i := 0 to 100 do
      begin
        if  b then
        begin
          Application.ProcessMessages ;
          Sleep(100) ;
          Form1.Label1.Caption := IntToStr(i) ;
        end
        Else
        Begin
          Exit ;
        end ;
      end ; }end.
      

  16.   

    if b then
          begin
            Application.ProcessMessages ;
            Sleep(100) ;
            s := FieldByName('cpkkc_jxname').AsString ;
            SendMessage(Form1.Handle, WM_SETTEXT, 0, Integer(PChar(S)));        
            Next;
          end;最简单的就是消息了。消息是同步的,所以不用考虑它是否会线程冲突ADOQuery1.Free ;
    CoUninitialize ;将Free放在Uninitialize之前。
      

  17.   

    嗯嗯 ... ,又这么多错 ...... :) 我用那个全局 Boolean 是因为 :如果单单用 TerminateThread() ,并不能终止线程,后来加了一个这个对了:) ,异步方式怎么用?
      

  18.   

    谢谢各位先。to copy_paste(木石三) :
    我目前也是这样处理的(置线程某个变量为False),但是取消后如果要再进行查询操作,会失败,除非这个线程执行完刚才的查询语句。这才是问题所在啊。to wjlsmail(计算机质子) :
    用Windows API TerminateThread()会产生资源泄漏。vcl的Thread中有一个FTerminated变量,它的Terminate方法就是简单地设置FTerminated为True,并不能即时终止线程。另外一个问题,好像各位都忽略了,即查询线程中的TDataSet对象不能在其他的线程中使用,因为会产生(无效的接口?我忘了)的错误,在移动数据库光标到一定位置后。
      

  19.   

    vcl的Thread中有一个FTerminated变量,它的Terminate方法就是简单地设置FTerminated为True,并不能即时终止线程。------------------------------------这个知道也测试过的,那段代码可以终止的 . 不过您所说的资源泄漏没注意过,Study :)
      

  20.   

    DataSet应该可以啊,我以前写过测试程序,也试过将它赋给主线程的TDataSource,也能够正常显示数据如果不行,你可以Clone一个DataSet, ADO有Clone这方法。ClientDataSet也有,Clone后,给主线程。至于你说失败后再查询。不如再起一个线程。主线程管理这些线程的生存期,如用一个TList管理这些线程,Create时List.Add(Thread), OnTerminate时,Remove(Thread),然后取消就不管了,如果再查询只是增加一个线程而已:D:D:D,(还没想到好方法)
      

  21.   

    to copy_paste(木石三) :
    能简单说说3层的异步处理吗?
      

  22.   

    to wjlsmail(计算机质子) :
    你到http://expert.csdn.net/Expert/TopicView1.asp?id=1511931跟一下,100分相送。
      

  23.   

    MIDAS结构本身不支持异步,你只能是使用自己写接口函数自己做(我也没试过,也不知行不行:D:D)
    Server:
      //客户发SQL命令过来,开始异步执行。
      BeginQueryData(const SQLCommand: WideString);
      //命令发过来后,客户不断的调用ReadQueryData,将已经取得的数据放到Data中。
      //Data可以是自己的格式,或什么什么数据类型。
      ReadQueryData(var Data: OleVariant);
      //取消操作。
      CancelQueryData;
      //完成操作
      EndQueryData;我现在也搞不通ReadQueryData怎么取得DataSet.Open后,已经填充了多少数据,或者它里面的数据是否填充到DataSet中了没?今天可能要进行申请XXX没心情试了
      

  24.   

    好像我就没见过三层中使用异步方式的,用MIDAS技术
      

  25.   

    好像COM有回调函数可以使用,不过我也不熟,如果可以实现,可以将ReadQueryData作为回调传给BeginQueryData,好像好点
    俺对MIDAS理解有限,从来没怎么做过东西
      

  26.   

    有几个问题:
    一、不要用API去终止线程,它是会造成MEMORY LEAK的,因为它不会同时FREE线程对象。
    二、最好不要用全局变量或成员变量来进行线程间通信,否则在多CPU在机器上跑你就会死得很难看,要用Event/Mutex/Semaphore等才可靠。
    三、在三层的客户端即使终止了客户端的查询也没有用,中间层仍在占用资源进行查询,而且如果强行终止客户端线程还可能会导致中间层出错,或scktsrvr等代理程序出错。
      

  27.   

    呵呵。。这样试试:
     
    TThreadStatus = (tsIdle, tsBusy);TMyThread = class(TThread)
    private
      FStatus: TThreadStatus;
    public
      property Status: TThreadStatus read FStatus;
    end;procedure TMyThread.Execute;
    var
      msg: TMsg;
    begin
      FStatus := tsIdle;
      while not Terminated do
        if GetMessage(msg, 0, 0, 0) do
        begin
          FStatus := tsBusy;
          case msg.message of
            ...//to you code...
          end;
          // 处理完成,做回调,再将FStatus置闲
          FStatus := tsIdle;
        end;
    end;然后主线程起动一个线程或多个线程,查Status是否Idle,不然就将任务给那个线程。然后,然后就不管它了你的是不是这个意思?
      

  28.   

    to  copy_paste(木石三):
    我的确是这个意思,但是我想能够取消服务器端正在进行的查询操作,因为我注意到,如果正在查询而结束进程(没有发现出错提示),我发现服务器端的查询自动终止了,我要的就是这个效果。
      

  29.   

    我以为CSDN没高手了!
    看样子是分流了!
    看看,祖国大地一片红!
    我这个一颗星星也来附庸风雅一下!
    : )
      

  30.   

    这种情况用消息循环有点浪费的说,建议用Event,主线程要处理时就PULSE一下EVENT,线程就处理,处理完了就WAITFOR这个EVENT即可挂起,等下一次主线程PULSE唤醒。
      

  31.   

    to Raptor(猛禽):
    能否给个小的示例
      

  32.   

    TMyThread = class(TThread)
    private
      FMyEvent : TEvent;
      //  other info
    public
      property MyEvent: TEvent read FMyEvent;
      //  other info/methods
    end;procedure TMyThread.Execute;
    begin
      While Not Terminated Do
      Begin
        If ( FMyEvent.WaitFor( $FFFFFFFF ) = wrSinaled ) Then // 线程会在这里挂起,不占用资源,直到MyEvent被Set
          // do something
        Else
          Terminate;
      End;
    End;
    MainForm:
    ...
      //  Set info
      MyThread.MyEvent.Set;
      MyThread.MyEvent.Reset;
      //  上两句等效于Pulse一下,然后线程就开始一次查询,查完线程就挂住了
    end;
      

  33.   

    to Raptor(猛禽):
    谢谢,试试先……
      

  34.   

    最好按正常的做法,在一次查询做完后再终止线程,即用Terminate方法。
    否则:
    三、在三层的客户端即使终止了客户端的查询也没有用,中间层仍在占用资源进行查询,而且如果强行终止客户端线程还可能会导致中间层出错,或scktsrvr等代理程序出错。若真的要强行终止线程,也不要用API来做:
    一、不要用API去终止线程,它是会造成MEMORY LEAK的,因为它不会同时FREE线程对象。建议做法:
    不要用TThread类,用RTL的BeginThread来实现
      

  35.   

    具体原因见TThread的WaitFor函数的源码。TThread在Destroy时调用WaitFor去等待当前线程结束(其中还包括与其它线程通信,以防死锁的一些东东),直接Free线程,就会等待,如果用API去终止再来FREE,因为它的HANDLE已经无效,WaitFor时又会出错。
    只能不用TThread了:<
      

  36.   

    多层结构中,使用ADO的异步方式,可以终止中间层和数据层的通讯。 但是如果中间层从数据层获取的记录多达几万条,在客户端只通过 ClientDataSet.Open 来获取数据的情况下,想终止这个操作是不可能的! 因为获取这几万条记录的操作实际上是由 IAppServer.AS_GetRecords() 这一个方法完成的。 这个远程调用是最耗时的(特别是当要下载的纪录有很多的时候)。  那么,如何终止这个远程调用呢?我想不出办法。 但是如果想放弃正在执行的查询操作,倒还是可以。这就是采用MIDAS的少量多次获取数据的方法,用一个循环在每次获取少量数据后,就检测是否要终止操作!  检测的所采用的方法上面各位高人都说得很多了!^_^
      

  37.   

    另外一个问题:http://expert.csdn.net/Expert/topic/1522/1522866.xml,
    ADOStoreProc无法调用参数类型为text的存储过程
      

  38.   

    Midas使用SocketConnection的过程解释:SocketConnection实际上在客户端生成了一个支持IDispatch接口的代理,这个代理对象实现了通过Socket调用远程服务器上面的过程,实际上,就是它实现了一个Invok的方法,他会通过Socket发送Invok的参数给服务器程序,然后一直等待服务器的返回数据。这个过程是同步的,因为如果异步的话,就需要服务器返回结果的时候指出是给哪一个Ivoke的,这是有困难的,除非增加额外的数据指出是那一个Invok,还需要使用缓存来缓存返回结果,这些做起来都是比较困难的。实际上简单的方法就是使用多个SocketConnection,来实现异步调用,这是最简单的方法,也不会增加太多的开销。通过我对服务器程序(SckSrv)的研究,我知道,一个连接,服务器端都会增加一个线程为这个连接服务。因此和实现一个SocketConnection的异步调用是等价的。在服务器端的ADOConnection使用异步调用也是无理的,
    1、任何调用在进入同一个com的时候,都会进入临界区,其他线程都被阻塞,实际上是不允许他们同时进入同一个Com的。
    2、ADOConnection异步调用,实际上你也不可能立刻返回,这时候也不允许其他线程进入这个Com,因为ADOConnection这时候是存在状态的,如果你立刻退出临界区,那么其他线程进入,就会破坏你先前的调用。这样使用异步调用毫无意义。最好的解决办法,就是用多个SocketConnection。
      

  39.   

    补充:SocketConnection是多线程安全的,就是多个线程可以同时调用他的方法,简单的说,就是可以在不同的线程中对TClientDataSet操作,这是安全的,但是,SocketConnection通过消息机制,把不同线程的操作进行了串列化,使用多线程等同于单线程,但是,主线程有机会初始化他的界面。我不太清楚DataSource是否多线程安全,VCL界面元件原则上是多线程安全的,应该没有问题,但是困难的是在于TClientDataSet打开的时候存在主从关系,LookUp字段等等,这时候存在数据产生需要同步的问题,有些数据需要先取得,有些需要后取得。用多线程,这是比较困难的。