[英]Delphi: Verify DataSnap connection via TThread
我們有一個應用程序,用戶可以在其中與我們交談,它工作正常,他創建了新的對話,我們聊天,沒關系。 但是,在開始聊天之前,他需要連接到DataSnap Server,這就是我要創建線程的地方。 每隔5分鍾,計時器將觸發他的事件以創建線程並嘗試在服務器上進行連接,如下所示:
我的主題:
unit UThreadSnapConnection;
interface
uses
System.Classes, System.SysUtils, Data.SqlExpr;
type
TThreadSnapConnection = class(TThread)
private
FSnap: TSQLConnection;
procedure TryToConnect;
protected
procedure Execute; override;
constructor Create;
public
DMSnap: TSQLConnection;
HostName: String;
Port: String;
end;
implementation
{ TThreadSnapConnection }
constructor TThreadSnapConnection.Create;
begin
inherited Create(True);
FreeOnTerminate := True;
end;
procedure TThreadSnapConnection.TryToConnect;
begin
try
FSnap := DMSnap.CloneConnection;
FSnap.Connected := False;
try
FSnap.Connected := True;
except
end;
if FSnap.Connected then
DMSnap.Connected := True;
finally
FreeAndNil(FSnap);
end;
end;
procedure TThreadSnapConnection.Execute;
begin
Synchronize(TryToConnect);
end;
end.
我的計時器:
procedure TMyDataModuleSnap.TimerSnapTimer(Sender: TObject);
var
MyThread: TThreadSnapConnection;
begin
if not(MySQLConnection.Connected) then
begin
MyThread := TThreadSnapConnection.Create;
MyThread.DMSnap := MySQLConnection;
MyThread.HostName := 'localhost';
MyThread.Port := '211';
MyThread.Resume;
end;
end;
我正在做的是嘗試連接到服務器,如果它可以正常工作,那么它將使我的數據模塊連接。
我的問題是,每次上線
FSnap.Connected := True;
執行它會使應用程序凍結1〜2秒鍾,而我創建線程的原因是不凍結。 據我所知,它不應該打擾所有應用程序,因此我開始認為這可能是將Connected屬性設置為True時所做的工作,無論該屬性是否為線程,它都會凍結。
嘗試連接時有什么方法不凍結嗎?
這是我的第一個線程,也許我只是誤解了一些東西,這不是線程的工作方式,但是,如果不是,那我就需要知道,或者至少要了解我在做什么錯。
編輯:我正在做的測試是,我在不啟動服務器的情況下啟動應用程序,因此它將嘗試連接失敗,並且我的數據模塊也將無法連接。
有兩種選擇:
TTimer
的OnTimer
事件時,您可以考慮在主線程之外創建實例 TThread
類實例 以下內容適用於#2。
在線程的Execute
過程中使用TEvent
,可以在執行下一個代碼塊之前等待FInterval
時間。
當Terminated
屬性設置為True
,此方法允許Execute
方法在間隔計數期間也立即返回,這與采用TThread.Sleep(FInterval);
調用,它將在指定的時間內凍結線程本身。
完成后,可以選擇使用TNotifyEvent
通知主線程。
TMyThread = class(TThread)
private
FInterval: Integer;
FTerminateEvent: TEvent;
protected
procedure Execute; override;
procedure TerminatedSet; override;
public
OnEndJob: TNotifyEvent;
constructor Create(Interval: Cardinal; CreateSuspended: Boolean);
destructor Destroy; override;
end;
constructor TMyThread.Create(Interval: Cardinal; CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
FInterval := Interval;
FTerminateEvent := TEvent.Create(nil, False, False, '');
end;
destructor TMyThread.Destroy;
begin
FTerminateEvent.Free;
inherited;
end;
procedure TMyThread.TerminatedSet;
begin
inherited;
FTerminateEvent.SetEvent;
end
procedure TMyThread.Execute;
begin
while not Terminated do begin
//do your stuff
//notify your connection to the main thread if you want
if Assigned(OnEndJob) then
Synchronize(procedure
begin
OnEndJob(Self);
end);
//wait fo some amount of time before continue the execution
if wrSignaled = FterminateEvent.WaitFor(FInterval) then
Break;
end;
end;
不要synchonize你想在一個線程中執行的代碼:Delphi中syncronized塊調用線程總是執行。
我本來希望發表評論而不是回答,但是我缺乏聲譽。 閱讀以下內容時應考慮的一些事項。
在兩行之間閱讀時,您似乎已連接到本地SQL Server。 訪問很少會導致連接斷開,因此您設置了一個計時器,每5分鍾檢查一次,並在必要時重新建立連接。
此方法有效,但是您發現連接嘗試會阻止程序執行,直到建立連接為止,因此您希望將此操作移至工作線程。
正如fantaghirocco所說,“同步”使代碼在主程序線程中運行。 我的理解是,此代碼在處理完主線程中的所有消息之后運行,因此您可以通過使計時器發布消息以及關聯的消息處理程序調用TryToConnect(在這種情況下以主窗體聲明TryToConnect)來實現相同的結果。
同步是允許線程與主線程進行交互的最簡單方法,而不必擔心兩個或多個線程同時訪問同一對象。
為了防止連接過程阻塞主程序線程,必須在TThread后代的Execute方法中設置MySQLConnection Connected屬性(未封裝在對Synchronize的調用中)。
但是這樣做會帶來工作線程和主程序同時訪問MySQLConnection的風險。 為了防止這種情況,您需要引入一個關鍵部分或類似內容。 如果不熟悉,請在RAD Studio幫助中檢查TCriticalSection。 有一個關於關鍵部分的部分和一個示例。
然后,主程序和線程都會將對MySQLConnection的所有調用封裝在關鍵部分try finally塊中:
FLock.Acquire;
try
{code accessing MySQLConnection goes here}
finally
FLock.Release;
end;
其中FLock是TCriticalSection對象。
任何試圖獲取FLock的線程(如果已被另一個線程獲取)將被阻止,直到釋放FLock。 這意味着僅當工作線程已經在嘗試連接時用戶嘗試訪問MySQLConnection時,主線程才會被阻止。
更新:
首先,下面是一個由兩個單元組成的簡單程序; Unit1包含主窗體(創建新應用程序時將顯示的內容)。 第二個單元Unit2包含一個線程。 由於您的線程似乎在一個單獨的單元中,因此我這樣做了。
我已經在TForm1中添加了一個按鈕和一個關鍵部分(在Uses子句中添加了System.SyncObjs)。 在Button1的click事件中,我創建一個TMyThread實例(在您的代碼中,它將由timer事件處理):
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
FLock: TCriticalSection;
end;
var
Form1: TForm1;
implementation
uses Unit2;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
TMyThread.Create;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FLock := TCriticalSection.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FLock.Free;
end;
Unit2包含線程。 execute方法是一炮而制。 將Unit1添加到實現中的uses子句中,以使代碼可以訪問Form1變量:
type
TMyThread = class (TThread)
protected
procedure Execute; override;
public
constructor Create;
end;
implementation
uses Unit1;
{ TMyThread }
constructor TMyThread.Create;
begin
inherited Create (False);
end;
procedure TMyThread.Execute;
begin
with Form1 do begin
FLock.Acquire;
try
{access MySQLConnection methods here}
finally
FLock.Release;
end;
end;
end;
當您運行此簡單程序並單擊Button1時,將創建一個單獨的線程並運行execute方法,然后銷毀該線程。 每次您單擊Button1都會重復此過程。
如果放在在1單元斷點MyThread := TMyThread.Create
線,並在UNIT2另一個斷點在FLock.Acquire
行,運行程序,並單擊Button1,代碼將停止在主線程; 左窗格中顯示的線程ID。 如果單擊F9繼續執行程序,它將在Unit2斷點處停止。 您會注意到線程ID現在有所不同,並且IDE底部的“線程狀態”窗口現在列出了這個額外的線程。 當再次按F9鍵時,此新線程消失。
該程序不執行任何操作,但是您將在該線程中運行所需的任何MySQLConnection代碼放在“嘗試最終”塊中的注釋處。
在主線程中,無論何時訪問MySQLConnection的方法,您都還需要將它們封裝在FLock try finally塊中。 例如,如果您有一個TClientDataSet連接到一個TDataSetProvider,而TDataSetProvider連接到了一個連接到MySQLConnection的TSQLDataSet,則打開TClientDataSet必須封裝在此FLock中。最后嘗試:
begin
FLock.Acquire;
try
CDS.Open;
finally
FLock.Release;
end;
end;
其中CDS是TClientDataSet。
您打算在線程中運行的代碼基本上會關閉連接並重新打開它。 關鍵部分的一個好處(如果配置正確,並且對MySQLConnection的所有訪問都受到關鍵部分的保護),它將防止在用戶查詢過程中關閉連接。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.