2014-04-21 53 views
1

我有一個Query並在我的Thread中打開它。它工作正常,我不想使用Synchronize,因爲Synchronize使主Form不響應,而Query未完成獲取。 當關閉Form打擊錯誤顯示:當在線程中打開查詢時關閉窗體時出錯(德爾福)

系統錯誤。編號:1400無效的窗口句柄

type 
    TMyThread = class(TThread) 
    public 
    procedure Execute; override; 
    procedure doProc; 
    end; { type } 
. 
. 
. 
procedure TMyThread.doProc; 
begin 
    Form1.Query1.Open; 
end; 

procedure TMyThread.Execute; 
begin 
    inherited; 

    doProc; 
end; 
. 
. 
. 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
    thrd := TMyThread.Create(True); 
    thrd.FreeOnTerminate := True; 
    thrd.Resume; 
end; 

注:Query有很多紀錄。

+0

不要把查詢放在窗體上。使其成爲本地執行。使其與分鐘線保持隔離。 –

+0

你的線程是由於它的'FreeOnTerminate'概念像扔紙飛機。你應該控制它。此外,表單中Query1對象的訪問可能不安全,很可能是您的問題的原因。最後,如果它是一個'TQuery'對象,那麼你使用的是過時的BDE,你可以升級到更新的東西。 – TLama

回答

2

問題是,VCL是而不是線程安全。
爲了讓查詢與所有其他事情並行執行,您必須將其與表單分離。

這意味着你將不得不使用代碼在運行時創建查詢:

type 
    TMyThread = class(TThread) 
    private 
    FQuery: TQuery; 
    FOnTerminate: TNotifyEvent; 
    public 
    constructor Create(AQuery: TQuery); 
    destructor Destroy; override; 
    procedure Execute; override; 
    procedure doProc; 
    //Add an event handler to do cleanup on termination. 
    property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate; 
    end; { type } 

constructor TMyThread.Create(AQuery: TQuery); 
begin 
    inherited Create(True); 
    FQuery:= AQuery; 
end; 

procedure TMyThread.doProc; 
begin 
    FQuery1.Open; 
    Synchronize(
    //anonymous method, use a separate procedure in older Delphi versions 
    procedure 
    begin 
     Form1.Button1.Enabled:= true; //reenable the button when we're done. 
    end 
); 
end; 

procedure TMyThread.Execute; 
begin 
    inherited; 
    doProc; 
end; 

destructor TMyThread.Destroy; 
begin 
    if Assigned(FOnterminate) then FOnTerminate(Self); 
    inherited; 
end; 

OnClick爲Button1,你會做到以下幾點:

type 
    TForm1 = class(TForm) 
    private 
    AQuery: TQuery; 
    ... 
    end; {type} 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    Button1.Enabled:= false; //disable the button so it cannot be started twice. 
    thrd.Free; 
    AQuery:= TQuery.Create; 
    AQuery.SQL.Text:= ..... 
    thrd := TMyThread.Create(AQuery); 
    thrd.OnTerminate:= MyTerminationHandler; 
    thrd.FreeOnTerminate:= False; 
    thrd.Resume; 
end; 

最後清理代碼分配給線程的終止處理程序。 如果你在線程中銷燬查詢,那麼你不能使用FreeOnTerminate:= true,但你必須自己釋放線程。

procedure TForm1.MyTerminationHandler(Sender: TObject); 
begin 
    FreeAndNil(AQuery); 
end; 

警告
此代碼只會工作,如果你開始1個線程。
如果你想啓動這個線程多次(即運行在同一時間多個查詢),你必須創建線程的數組,例如:

TQueryThreads = record 
    MyThread: TMyThread; 
    MyQuery: TQuery; 
    constructor Create(SQL: string); 
end; {record} 

TForm1 = class(TForm) 
private 
    Threads: array of TQueryThreads; 
.... 
end; {TForm1}  

注意,該代碼將不會在工作BDE,因爲該庫不支持同時運行多個查詢
如果你想這樣做,你將不得不使用ZEOS或類似的東西。

按TLama的建議:
我建議BDE TQuery組件切換到ADO或下載類似ZEOS components。 BDE是非常過時的,並且有很多怪癖永遠不會被修復,因爲它不再被維護。

如果Form1已關閉,則剩下的唯一問題是清理連接。
如果這是您的主要形式,那真的沒有關係,因爲您的整個應用程序都會關閉。
如果它不是您的主要表單,則需要通過填寫OnCanClose處理程序來禁用關閉表單。

TForm1.CanClose(Sender: TObject; var CanClose: boolean); 
begin 
    CanClose:= thrd.Finished; 
end; 
+0

在踩下運行時關閉表單會導致麻煩,啓動新的MyThread會造成麻煩(您不能同時使用來自併發線程的連接)。這有很多不需要的副作用 –

+0

@SirRufo,這就是爲什麼我禁用了'Button1',所以你不能兩次啓動同一個動作。這不會阻止你啓動另一個BDE不喜歡的查詢。我已經添加了一個建議,以利用另一個** **支持多個運行查詢的數據庫庫。 – Johan

+0

是的,你必須寫很多代碼才能防止副作用(禁用所有操作,關閉窗體),或者只是使用模態窗體並且沒問題。兩者都可以工作,但我喜歡懶惰的程序員保持簡單和可維護的方式 –

1

您應該阻止MainThread中的任何操作(用戶和程序)而不阻止它。這可以通過用戶不能關閉的模式形式輕鬆完成。

線程可以做任何事情,只要它需要和最後(同步)的步驟是關閉該模式窗體。

procedure OpenDataSetInBackground(ADataSet : TDataSet); 
var 
    LWaitForm : TForm; 
begin 
    LWaitForm := TForm.Create(nil); 
    try 
    LWaitForm.BorderIcons := []; // no close buttons 

    TThread.CreateAnonymousThread(
     procedure 
     begin 
     try 
      ADataSet.Open; 
     finally 
      TThread.Synchronize(nil, 
      procedure 
      begin 
       LWaitForm.Close; 
      end); 
     end; 
     end); 
    try 
     LWaitForm.ShowModal; 
    finally 
     LWorkThread.Free; 
    end; 
    finally 
    LWaitForm.Free; 
    end; 
end; 

但是你必須要小心這一點,你不應該嘗試做啓動多個並行線程使用此代碼,除非你真的知道自己在做什麼。

+0

爲什麼打開一個模態窗體然後創建一個單獨的線程。這是沒有道理的,你可能會阻止所有的行動,並忘記線程。 – Johan

+0

因爲避免沒有響應的應用程序?這是這個問題的主要觀點。 –

+1

模態對話框會與無響應的應用程序一樣糟糕。這讓我等待機器,這是糟糕的UI設計。機器必須等待我,而不是相反。如果我要閃光模式對話框,我可以放棄線程並禁用所有按鈕和邊框,請等待,我處於愚蠢的模式對話框。編程會更容易。 – Johan