次问题困扰了我N年,如果哪位大侠帮我解决此问题小弟将满分赠送!!
---------------------------------
  问题如下:我想用BDE和多线程连接Oracle(或SQLServer)数据库,因为数据库连接是很耗时的。
  创建了2个多线程类,一个专门进行数据库连接、另一个专门进行连接进度显示。
  结果程序一运行到FDB.Open;进行数据库连接主线程就会失去控制权,导致主线程死等现象。请高手帮忙!!!急!!!
--------以下代码Delphi5编译通过-------------------------
unit Unit2;interfaceuses
  Windows, Messages, Classes, SysUtils, ComCtrls, dbTables;type
  //进度线程
  TProgressThread = class(TThread)
  private
    FDB: TDatabase;
    Fpb: TProgressBar;
    FConnected: boolean;
    iPos: integer;
    procedure DoWhatVCLAction;
  public
    procedure Execute; override;
    constructor Create(DB: TDatabase; pb: TProgressBar);
  end;  //数据库连接线程
  TDBConnectThread = class(TThread)
  private
    FDB: TDatabase;
    FConnected: boolean;
    FErrorStr: string;
    procedure DoWhatVCLAction();
    procedure DoDBConnect(out Connected: boolean; out ErrorStr: string);
  public
    procedure Execute; override;
    constructor Create(DB: TDatabase);
    property Connected: boolean read FConnected default False;
    property ErrorStr: string read FErrorStr;
  end;implementation{ TProgressThread }constructor TProgressThread.Create(DB: TDatabase; pb: TProgressBar);
begin
  FDB:= DB;
  Fpb:= pb;
  FConnected:= False;
  iPos:= pb.Position;
  FreeOnTerminate:= true;
  inherited Create(False);
end;procedure TProgressThread.DoWhatVCLAction;
begin
  if not FDB.Connected then
  begin
    if Fpb.Position <> 100 then
      Fpb.StepIt
    else
      Fpb.Position:= 0;
    if Terminated then Fpb.Position:= 100;
  end
  else FConnected:= true;
end;procedure TProgressThread.Execute;
begin
  while not FConnected do
  begin
    Inc(iPos);
    synchronize(DoWhatVCLAction);
    if Terminated then exit;
  end;
end;{ TDBConnectThread }constructor TDBConnectThread.Create(DB: TDatabase);
begin
  FDB:= DB;
  FreeOnTerminate:= true;
  inherited Create(False);
end;procedure TDBConnectThread.DoDBConnect(out Connected: boolean; out ErrorStr: string);
begin
  Connected:= False;
  try
    if not FDB.Connected then
    begin
      FDB.Open;
      Connected:= true;
    end else
      Connected:= true;
  except
    On E: Exception do ErrorStr:= E.Message;
  end;
end;procedure TDBConnectThread.DoWhatVCLAction();
begin
  DoDBConnect(FConnected, FErrorStr);
end;procedure TDBConnectThread.Execute;
begin
  synchronize(DoWhatVCLAction);
end;end.
//----调用是这样调的:----------------
//窗体上放Database1,Table1,DataSource1,DBGrid1,Button1
//并连接字符串已经设置好了,只要用线程类进行连接即可uses Unit2;{$R *.DFM}procedure TForm1.Button1Click(Sender: TObject);
var
  pro: TProgressThread;
  conn: TDBConnectThread;
begin
  pro:= TProgressThread.Create(Database1, Progressbar1);
  conn:= TDBConnectThread.Create(Database1);end;

解决方案 »

  1.   

    我把所有synchronize去掉就好使了,请问为什么?
    这样线程安全吗?请高手答复!
      

  2.   

    俺不懂线程,转发一个贴子,看能不能用的上。主  题:  怎么不能从线程中取出一个数据,关于数据多线程查询! 
    作  者:  skyclin ()  
    等  级:    
    信 誉 值:  100 
    所属论坛:  Delphi 基础类 
    问题点数:  60 
    回复次数:  5 
    发表时间:  2002-11-27 17:13:05 
       
     
       
    申请说明部分:
      TThreadQuery = class(TThread)
      private
        FQuery: TADOQuery;
        LI_I:integer;
        procedure GetCount;
      protected
        procedure Execute; override;
      public
        constructor Create(Query: TADOQuery;SQLString:string); virtual; 
      end;procedure TThreadQuery.GetCount;
    begin
       if FQuery.IsEmpty then
          LI_I:=0
        else LI_I:=FQuery.RecordCount;
    end;procedure TThreadQuery.Execute;
    begin
      try
       FQuery.Open;
       Synchronize(GetCount);
      except
        ShowMessage('Query Error');    { 线程异常 }
      end;
    end;constructor TThreadQuery.Create(Query: TADOQuery; SQLString:string);
    begin
      inherited Create(True);
      FQuery := Query;
      FQuery.Connection:=JLDMFrm.DBConnection;
      FQuery.SQL.Text:=SQLString;
      Resume;
      FreeOnTerminate := False;
    end;这是调用部分:
    function TheadQueryFunc(SQLString:string):integer;
    var
     Q1:TThreadQuery;
     LQ_Q:TADOQuery;
    begin
       LQ_Q:=TADOQuery.create(nil);
       Q1:=TThreadQuery.Create(LQ_Q,SQLString);
       result:=Q1.LI_I;
       Q1.Terminate; { 销毁之前终止线程执行 }
       Q1.Destroy;
    end;edit1.text:=inttostr(TheadQueryFunc(x));调用时edit1.text老是等于0;
    为什么,为什么?
    功夫不到家,望高手指点!!
      
     
     
     回复人: zwjchina(蒲石) ( ) 信誉:100  2002-11-27 17:25:00  得分:0 
     
     
      好象对线程对象的属性不能这样访问,我记不清楚了,
    这样,做个测试,
    在result := Q1.LI_I;
    前面加一句:sleep(100);
    如果结果还是0,那么你还是用全局变量吧
      
     
    Top 
     
     回复人: outer2000(天外流星) ( ) 信誉:100  2002-11-27 17:30:00  得分:0 
     
     
      function TheadQueryFunc(SQLString:string):integer;
    var
     Q1:TThreadQuery;
     LQ_Q:TADOQuery;
    begin
       LQ_Q:=TADOQuery.create(nil);
       Q1:=TThreadQuery.Create(LQ_Q,SQLString);
       result:=Q1.LI_I;
       Q1.Terminate; { 销毁之前终止线程执行 }
       Q1.Destroy;
    end;
    你的Lq——q就没有创建啊;
      

  3.   

    Top 
     
     回复人: wjlsmail(计算机质子) ( ) 信誉:100  2002-11-27 17:49:00  得分:0 
     
     
      可不可以这样改一下,由于在调用时传入的TADOQuery没有用,故:constructor TThreadQuery.Create(SQLString:string); 
    begin
      inherited Create(True);
      FQuery.Connection:=JLDMFrm.DBConnection;
      //也可以将FQuery定义为主线程的全局变量,只在这儿传入Sql.Text 
      FQuery.SQL.Text:=SQLString;
      Resume;
      FreeOnTerminate := False;
    end;
    调用部分:
    function TheadQueryFunc(SQLString:string):integer;
    var
     Q1:TThreadQuery;
    begin
       Q1:=TThreadQuery.Create(SQLString);
       result:=Q1.LI_I;
       Q1.Terminate; { 销毁之前终止线程执行 }
       Q1.Destroy;
    end;
      
     
    Top 
     
     回复人: wjlsmail(计算机质子) ( ) 信誉:100  2002-11-27 18:07:00  得分:0 
     
     
      还有,
        private
        
          LI_I:integer;
        
       怎么会传到外边呢? 是不是 ?
      
     
    Top 
     
     回复人: wjlsmail(计算机质子) ( ) 信誉:100  2002-11-27 18:42:00  得分:0 
     
     
      zwjchina(蒲石) 说的是对的  我试了一下:不论怎样定义LI_I,都传出来的是 0 ,一般在同一个线程的各个线程对象间传递参数使用线程自己定义的变量 。 我改了一下:
      
     调用:
     
       with TThreadOfCount.Create(Edit1.Text) do
        begin
          if Suspended then Resume;
        end;
       Label1.Caption := IntToStr(II); 线程定义:  TThreadOfCount = class(TThread)
      private
        procedure GetCount;
        { Private declarations }
      protected
        procedure Execute; override;
      public
        constructor Create(SQLString: string); virtual;
      end;
    var
      FFQuery : TADOQuery ;
      II : Integer ;implementationconstructor TThreadOfCount.Create(SQLString: string);
    var
      ADOC: TADOConnection;
    begin
      inherited Create(False);
      ADOC := TADOConnection.Create(Nil);
      with ADOC do
      begin
        ConnectionString := '...' ;
        LoginPrompt := False ;
        Connected := True ;
      end;
      FFQuery := TADOQuery.Create(Nil) ;
      FFQuery.Connection := ADOC ;
      FFQuery.SQL.Text := SQLString;
      Resume;
      FreeOnTerminate := False;
    end;procedure TThreadOfCount.GetCount;
    begin
      if FFQuery.IsEmpty then
        II := 0
      else
        II := FFQuery.RecordCount;
    end;procedure TThreadOfCount.Execute;
    begin
      { Place thread code here }
      try
        FFQuery.Open;
        Synchronize(GetCount);
      except
        ShowMessage('Query Error'); { 线程异常 }
      end;
    end;
    end.测试通过 ,但是对线程中使用参数不清楚,望大家指导
      

  4.   

    主  题:  关于多线程的结束 
    作  者:  cooling (cooling)  
    等  级:    
    信 誉 值:  100 
    所属论坛:  Delphi 网络编程/分布式开发 
    问题点数:  100 
    回复次数:  4 
    发表时间:  2002-11-24 15:28:02 
       
     
       线程大致是这样的constructor Tmythread.Create(RunStart: Boolean; p: PUDPData; FrmHandle: HWND);
    begin
       Handle := FrmHandle;  //主线程句柄
       UdpData := P;         //主线程传过来的需要处理的数据指针
       inherited Create(RunStart);
    end;procedure Tmythread.Execute;
    begin
      {Place thread code here}
      FreeOnTerminate := True;
      while Not Terminated do
      begin
        //……处理数据UdpData,时间比较长
        //涉及到主线程中的可视控件用了Synchronize方法同步    Suspend;       //处理完后挂起,等待下次调用  end;
    end;
    主线程中:procedure TFrmMain.madeThread;//构造数据处理线程
    var
       i : Integer;
    begin
      setlength(RecvUDP,Thread_count-1);//创建Thread_count个数据缓冲区RecvUdp
      setlength(mythread, Thread_count - 1);//创建Thread_count个数据处理线程
      For i := 0 to Thread_count - 1 do//可能有很多线程
      begin
        new(RecvUDP[i]);
        mythread[i] := Tmythread.Create(True, RecvUDP[i], FrmMain.Handle);
           //一个数据处理线程对应一个数据缓冲区(创建后先挂起)
      end;
    end;当主线程中有数据要处理时唤醒一个线程来处理问题是我在结束程序时在主窗体的close中调用以下自定义程序FreeThread,终止线程出错:procedure TFrmMain.FreeThread;// 释放线程数组和数据缓冲数组
    var
       i : Integer;
    begin
       For i := 0 to Thread_count - 1 do
       begin
         mythread[i].Terminate;
         mythread[i].WaitFor;//到这里时程序死了,不响应
         dispose(RecvUDP[i]);
       end;
    end;
    我也试过这么写:procedure TFrmMain.FreeThread;// 释放线程数组和数据缓冲数组
    var
       i : Integer;
    begin
       For i := 0 to Thread_count - 1 do
       begin
         mythread[i].suspend;
         mythread[i].Terminate;
         dispose(RecvUDP[i]);
       end;
    end;//提示错误的指针,我想是线程Terminate后还没有真正结束,但主线程中的缓冲数组已经释放掉了。
    我应该怎样保证在关闭整个程序时把线程安全的释放掉??  
     
     
     回复人: stanely(俺是邢她汉子) ( ) 信誉:102  2002-11-24 16:25:16  得分:30 
     
     
      
    up  
     
    Top 
     
     回复人: d983074(d983074) ( ) 信誉:100  2002-11-25 15:15:21  得分:30 
     
     
      
    这是我写的线程代码
    procedure tmythread.excute;
    var
      EventSignaled:DWORD;
      EventsToWait:array [0..1] of Thandle;
    begin
      EventsToWait[0]:=CloseEvent;
      EventsToWait[1]:=YourEvent;
      while true do
      begin
        EventSignaled := WaitForMultipleObjects(2,@EventsToWait,False, INFINITE);
        case EventSignaled of
        Wait_Object_0:
          begin
            exit;
          end;
        Wait_Object_0+1:
          begin
            do your things; 
          end;
        Wait_Failed:exit;
        end;
      end;
      
     
    Top 
     
     回复人: d983074(d983074) ( ) 信誉:100  2002-11-25 15:17:13  得分:10 
     
     
      
    创建事件
    closeevent:=createevent(nil,false,false,nil);
    触发
    setevetn(closeevent);
    释放事件
    closeevent(closeevent);
      
     
    Top 
     
     回复人: findcsdn(findcsdn) ( ) 信誉:106  2002-11-25 18:16:33  得分:30 
     
     
      
    mythread[i].Terminate;
     mythread[i].WaitFor;  //到这里时程序死了,不响应上面的写法是对的,至于死的原因是因为,可能你还不了解 Terminate 过程的实质。 Terminate发放,作的唯一的事就是将线程 FTerminated:=True;
    所以你在线程内要不断的检测 self.terminated  是否为真,如果为真立即跳出工作过程,退出线程,这样WaitFor才能继续执行。例如: 有一个操作数据库的线程主函数。procedure tmythread.fill;
    begin
      while (not dataset.Eof) and (Not terminated) do  //判断terminated
      begin
        row:=sList.Items.Add;
        row.Data:=Pointer(Data.FieldByName('fpoemid').AsInteger);
        row.Caption:=data.FieldByName('ftitle').AsString;
        data.Next;
      end;
    end;
      

  5.   

    大家只要把我的代码粘贴运行就可以了,大家快帮忙!
    分数也许只给答对的一个人!Help,Help!!!!!!
      

  6.   

    你试用DbExpress吧, 或者第三控件: SQL Direct, 因为它可以让你需不需要在客户端建Databuffer. 其连接时间会比BDE为快.