線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:3726
推到 Plurk!
推到 Facebook!

Timer不能用,改用Thread會可行嗎

 
Andy Wu
一般會員


發表:17
回覆:25
積分:18
註冊:2004-02-25

發送簡訊給我
#1 引用回覆 回覆 發表時間:2007-03-03 11:48:25 IP:61.219.xxx.xxx 訂閱
寫了支程式,為了表示此台電腦仍在連線之中(意即尚未當機), 因此每3分鐘更新某一資料表
使用了TTimer, 發現若開啟某一TQuery, 獲取資料量過多, Open的時間會影響到TTimer的OnTimer的事件
造成超過3分鐘以上才去更新資料表

TThread我還不會寫, 不知道改用TThread會不會有不同的結果?
暗黑破壞神
版主


發表:9
回覆:2301
積分:1627
註冊:2004-10-04

發送簡訊給我
#2 引用回覆 回覆 發表時間:2007-03-03 12:33:24 IP:218.170.xxx.xxx 訂閱
兩個問題。
1.這個3分鐘一次的更新,有那麼的重要嗎?
如果很重要。應該是要做即時的。而不是3分鐘一次。
既然已經3分鐘一次了。那延遲一下又會怎樣?
2.TQuery 會慢到這個程度?有些不可思議。
你要看看你的 SQL 語法,是怎麼寫的。
那邊也是你可以改進的空間。

===================引 用 文 章===================
寫了支程式,為了表示此台電腦仍在連線之中(意即尚未當機), 因此每3分鐘更新某一資料表
使用了TTimer, 發現若開啟某一TQuery, 獲取資料量過多, Open的時間會影響到TTimer的OnTimer的事件
造成超過3分鐘以上才去更新資料表

TThread我還不會寫, 不知道改用TThread會不會有不同的結果?
Andy Wu
一般會員


發表:17
回覆:25
積分:18
註冊:2004-02-25

發送簡訊給我
#3 引用回覆 回覆 發表時間:2007-03-03 14:49:42 IP:61.219.xxx.xxx 訂閱
1. 其實這個更新, 是為了讓資料庫知道此一使用者仍在連線中, 每個使用者連線至資料庫時均會先做檢查
上線人數是有限制的, 有更新的算一個連線, 沒更新的當作他當機了不算一個連線, 所以為了證明自己是活著的
系統內的這支程式, 要三分鐘去更新一下這個資料表
或許有更好的做法, 目前我只想到在資料庫當中開個資料表, 每個連線建立之後, 先去資料表檢查是否超過連線數上限
如果沒有, 新增一筆, 每三分鐘更新一次最後的活動時間, 並且檢查其他已經存在的連線, 看有哪個連線是超過三分鐘
沒有更新最後活動時間的, 如果有就刪除他

2. TQuery 的資料量大且還得要運算, 已經用各種方法將其最佳化了, 開啟需要幾個小時都有, 所以這部份倒是不需費心了

===================引 用 文 章===================
兩個問題。
1.這個3分鐘一次的更新,有那麼的重要嗎?
如果很重要。應該是要做即時的。而不是3分鐘一次。
既然已經3分鐘一次了。那延遲一下又會怎樣?
2.TQuery 會慢到這個程度?有些不可思議。
你要看看你的 SQL 語法,是怎麼寫的。
那邊也是你可以改進的空間。

===================引 用 文 章===================
Kingron
中階會員


發表:1
回覆:51
積分:60
註冊:2005-09-14

發送簡訊給我
#4 引用回覆 回覆 發表時間:2007-03-03 15:42:54 IP:125.89.xxx.xxx 訂閱
Timer采用消息来实现,会阻塞VCL主线程消息队列,也就是说,必须等待消息处理完成,应用程序才会继续后面的处理过程。

你的方式最好采用后台线程来做。付一个利用Thread来实现的。
{ 提供类似TTimer组件的功能,不过采用线程和事件来完成 }
{ TTimer组件需要消息循环,在没有消息的时候或者异步的 }
{ 情况下,TTimer组件不再适用,用TTimerThread即可 }
TTimerThread = class(TThread)
private
FOnTimer: TNotifyEvent;
FEvent: TSimpleEvent;
FInterval: DWORD;
FSyncInvoke: Boolean;
FEnabled: Boolean;
FOnError: TNotifyEvent;
FReEntry: Boolean;
procedure SetEnabled(const Value: Boolean);
procedure SetInterval(const Value: DWORD);
procedure SetReEntry(const Value: Boolean);
protected
procedure DoTimer; virtual;
procedure DoError; virtual;
procedure Execute; override;
public
constructor Create;
destructor Destroy; override;

procedure Free; { Free方法是保证调用者不会错误的释放两次,因为Thread会自动Free }
procedure Reset; { 重新开始计时,当前这次定时事件不会触发 }
published
property Interval: DWORD read FInterval write SetInterval; { 单位是毫秒计算 }
property SyncInvoke: Boolean read FSyncInvoke write FSyncInvoke; { 同步还是异步调用OnTimer事件 }
property ReEntry: Boolean read FReEntry write SetReEntry; { 是否可以重入,默认否 }
property Enabled: Boolean read FEnabled write SetEnabled;

property OnTimer: TNotifyEvent read FOnTimer write FOnTimer;
property OnError: TNotifyEvent read FOnError write FOnError;
end;


/// 下面是实现部分
{ TTimerThread }
constructor TTimerThread.Create;
begin
inherited Create(True); { Suspend after create }
FreeOnTerminate := True; { Free On Terminiate }
FInterval := 1000;
FEnabled := False;
FSyncInvoke := True;
FReEntry := False;
FEvent := TSimpleEvent.Create;
end;

destructor TTimerThread.Destroy;
begin
FEvent.Free;
inherited;
end;

procedure TTimerThread.DoError;
begin
if Assigned(FOnError) then FOnError(Self);
end;

procedure TTimerThread.DoTimer;
begin
if Assigned(FOnTimer) and not Terminated then
try { 防止未处理的异常导致线程异常结束 }
FOnTimer(Self);
except
if SyncInvoke then
Synchronize(DoError)
else
DoError;
end;
end;

procedure TTimerThread.Execute;
begin
repeat
if FEvent.WaitFor(FInterval) = wrTimeout then { 必须是等待指定时间才调用 }
try
if ReEntry then { 如果可以重入,则创建一个线程运行代码之后,立即返回,保证精确计时 }
TRunThread.Create(DoTimer, FSyncInvoke)
else if FSyncInvoke then { 不可以重入,可能在计时上面没有那么精确,同步调用 }
Synchronize(DoTimer)
else
DoTimer;
except
end;
until Terminated;
end;

procedure TTimerThread.Free;
begin
Terminate;
FEvent.SetEvent;
end;

procedure TTimerThread.Reset;
begin
{ 复位计时标志 }
FEvent.SetEvent;
FEvent.ResetEvent;
end;

procedure TTimerThread.SetEnabled(const Value: Boolean);
begin
if Value <> FEnabled then
begin
FEnabled := Value;
if FEnabled then
Resume
else
Suspend;
end;
end;

procedure TTimerThread.SetInterval(const Value: DWORD);
begin
if Value <> FInterval then
begin
FInterval := Value;
if Enabled then Reset;
end;
end;

procedure TTimerThread.SetReEntry(const Value: Boolean);
begin
if Value <> ReEntry then
begin
FReEntry := Value;
if Enabled then Reset;
end;
end;



你可以设置ReEntry属性来保证每3分钟精确调用一次你的代码。

但如果每次你的代码都不能再3分钟能完成,我看你需要考虑调整你的程序结构,检讨一下你的程序效率和处理方式。

===================引 用 文 章===================
寫了支程式,為了表示此台電腦仍在連線之中(意即尚未當機), 因此每3分鐘更新某一資料表
使用了TTimer, 發現若開啟某一TQuery, 獲取資料量過多, Open的時間會影響到TTimer的OnTimer的事件
造成超過3分鐘以上才去更新資料表

TThread我還不會寫, 不知道改用TThread會不會有不同的結果?
------
超级猛料:http://kingron.delphibbs.com
syntax
尊榮會員


發表:26
回覆:1139
積分:1258
註冊:2002-04-23

發送簡訊給我
#5 引用回覆 回覆 發表時間:2007-03-03 16:04:43 IP:61.64.xxx.xxx 訂閱
1. 是什麼樣的需求需要這樣設計?
有很多實作都這樣做:如果使用者 x 分鐘沒有回應,就斷線
且這也是網路上絕大部分採用的方法
姑且不論是否真的很好,起碼採用至今,還是最多人所使用的方法

你為什麼不用?反而要在使用者端做這件事?

資料庫可以設定連線數上限,沒有 stay connected 的就算斷線,請問你用的是哪家資料庫,怎會無法使用這基本功能?
資料庫也可以管理使用者,這些早已有的功能,建議你還是用一下吧!

覺得你可能沒有弄清楚你要做的事情的本質,所以設計得很複雜,不然就是你說的不夠清楚
{是為了讓資料庫知道此一使用者仍在連線中} 覺得這一句怪怪的

2.{TQuery 的資料量大且還得要運算, 已經用各種方法將其最佳化了, 開啟需要幾個小時都有}
更讓人覺得你的「基本」設計上有問題
可以說明是什麼樣的程式會這樣?要幾個小時? DNA 資料庫 matchimg ?
我曾做過一個程式,計算一個數值,樹狀的結構,每一點往後推算 15 個點,在累加回來得到所需
剛開始時,恩,當樹高很高時,也是要好幾個小時,最後,根本算不完,因為 每一點往後推算 15 個點
不把樹全算完,是不可能結束,但當樹高 20 層時,可以想像有多少計算?
後來,解決方式,是在加入節點時,就先算掉部分,並存下來,結果每次開啟都不到 30 秒
現在的人都沒耐心, 3 分鐘就算多,你要幾個小時,肯定是個超級大案子,需要超大量分析之類的嗎?
可以說明是什麼樣的程式會這樣?要幾個小時? DNA 資料庫 matchimg ?








Kingron
中階會員


發表:1
回覆:51
積分:60
註冊:2005-09-14

發送簡訊給我
#6 引用回覆 回覆 發表時間:2007-03-03 16:18:50 IP:125.89.xxx.xxx 訂閱
再仔细看了一些楼主的问题,实际上就是需要一个心跳命令而已。

TQuery不是在Timer中运行的,但会影响到正常的心跳命令运行,因为TQuery在运行的时候,Timer的消息事件无法正常运行,因为必须等待TQuery所在的事件处理代码完成后才能执行。

这个问题完全可以用后台线程来完成,利用上面给出来的TimerThread就好了。用法和Timer类似。只要给OnTimer赋值就可以了。注意如果你的心跳命令当中需要用到VCL,也会影响到时间进度的。因为VCL线程同步同样会消息阻塞的。
------
超级猛料:http://kingron.delphibbs.com
Andy Wu
一般會員


發表:17
回覆:25
積分:18
註冊:2004-02-25

發送簡訊給我
#7 引用回覆 回覆 發表時間:2007-03-03 17:33:50 IP:61.219.xxx.xxx 訂閱

===================引 用 文 章===================
procedure TTimerThread.Execute;
begin
? repeat
??? if FEvent.WaitFor(FInterval) = wrTimeout then { 必须是等待指定时间才调用 }
??? try
????? if ReEntry then { 如果可以重入,则创建一个线程运行代码之后,立即返回,保证精确计时 }
??????? TRunThread.Create(DoTimer, FSyncInvoke)
^^^^^^^^^^^^^
????? else if FSyncInvoke then { 不可以重入,可能在计时上面没有那么精确,同步调用 }
??????? Synchronize(DoTimer)
????? else
??????? DoTimer;
??? except
??? end;
? until Terminated;
end;
===================引 用 文 章===================

TRunThread 請問是什麼物件 找不到說...
Andy Wu
一般會員


發表:17
回覆:25
積分:18
註冊:2004-02-25

發送簡訊給我
#8 引用回覆 回覆 發表時間:2007-03-03 18:12:30 IP:61.219.xxx.xxx 訂閱
===================引 用 文 章===================
1. 如果使用者 x 分鐘沒有回應,就斷線
你為什麼不用?反而要在使用者端做這件事?
資料庫可以設定連線數上限
Ans: 抱歉我沒說清楚, 系統是採用三層式的架構, N台Ap Server對應一台DB Server
確認使用者是否連線中或早已當機, 由Client端發出訊息, 由Ap Server去更新資料庫 , 同一時間, Ap Server
也去檢查資料庫當中有哪些連線是超過三分鐘都沒有更新的狀態
在下只是有這樣的初步構想, 若有更好更便利的做法那當然是更好
至於DB的連線數上限, 因為三層式架構應該無從得知有多少Client端吧

2.可以說明是什麼樣的程式會這樣?要幾個小時? DNA 資料庫 matchimg ?
Ans: 在下只是小小工程師, 公司的ERP系統, 無力修改所有的子系統與報表, 除了Stored Procedure & Trigger,
很多資料還是得透過Script的運算再運算, 資料庫雖然不算龐然大物, 但是幾十萬筆的資料交叉查詢後運算,
能夠縮短開啟時間的最佳化方式大多都測試運用了, 還是得需要一段時間才能開啟某些報表
正因為如此, 在等待報表開啟的同時, 並不能被系統判定為[當機中], 所以才會需要TTimer每三分鐘告訴DB,
目前這個Client還在執行中, 但測試結果是TTimer會受到TQuery的影響無法正確運作, 所以才想改用TThread
===================引 用 文 章===================
Kingron
中階會員


發表:1
回覆:51
積分:60
註冊:2005-09-14

發送簡訊給我
#9 引用回覆 回覆 發表時間:2007-03-03 18:15:19 IP:125.89.xxx.xxx 訂閱
抱歉,没有仔细检查,代码是摘录出来的。
TRunThread因为包含其他的功能,代码也比较多,就不贴出来了。
在这里主要是利用它来单独生成一个线程来运行指定的代码,这样可以保证一定是每隔指定的时间就会调用一次。
因为如果直接在原来的线程当中运行代码,可能会不保证一定是指定的时间运行一次指定的代码,因为代码运行也需要消耗时间的。

你可以去掉这个功能,因为你的代码不需要那么精确,只是一个心跳而已,要求可以不用那么高。可以把ReEntry相关的代码都移掉。

===================引 用 文 章===================
TRunThread 請問是什麼物件 找不到說...
------
超级猛料:http://kingron.delphibbs.com
暗黑破壞神
版主


發表:9
回覆:2301
積分:1627
註冊:2004-10-04

發送簡訊給我
#10 引用回覆 回覆 發表時間:2007-03-03 20:01:00 IP:218.170.xxx.xxx 訂閱
那請問一下。你的程式是在那一個地方?
client?
Ap server?
DB server?

DB 連線有上限,如果你是 client 也不應該是你要擔心的事。
我不知道你做這樣的動作是為了什麼。
為了保謢嗎?如果是為了保護,那你要犧牲效率是一定的。
如果不是,我真的想不懂你怎麼會搞出這麼複雜的問題。
首先。你對 TCP/IP 不熟。所以你會有這方面的問題。
因為你可能是用Borland內建的Database元件來做 3Tier 動作。
如果你是用 Socket 方式。你可以找到它的 event 就可以解決了。
不必去做”確認”。

再者。如果你還需要”確認”這件事。你也應該是在 AP SERVER 做這件事。
在 AP SERVER 那邊每隔多久就要去對 clinet 送個 "ping timestamp"
讓 client 回應 "pong timestamp" 來處理。
而且。也不是每個 client 要定時去做。
對於已經剛送過 message 的 client 就要把送 ping 的時間延後再去做 ping pong 動作。
我就不知道你所用的元件是否有這種功能。

再來看到你又說到去AP SERVER更新你有多少個連線。這個資料又想要送回DB SERVER?
如果 DATABASE SERVER 真的有你說的這麼忙,這麼慢。
那這個資料是不是應該要在 AP SERVER 自己用個變數去處理它就好。不要老是要去煩
DB SERVER?如果AP SERVER重新啟動時,才去跟DB SERVER溝通說目前的資料表有那些。
不是應該用這種方式來解決問題嗎?怎麼是什麼事都塞給夠忙的DB SERVER呢?

再說。你的限制如果是DB SERVER的連線數。所以你才用 AP SERVER 的話。
那你的AP是接受N個 client 的 connect 而 AP 對 SQL SERVER 應該是
Alway one connect 的模式。
每個 AP Server 都只有一個 connect 到 db server 去。
那更不會產生你所說的問題呀。

至於 SQL 你說都OK?
我不認為。
你有加上 INDEX 了嗎?
你有把 ORDER 拿掉了嗎?
你知道那個”語法”SQL SERVER是怎麼處理嗎?
對於大的資料運作。你更應該要小心的去下每一句語法。

>Ans: 抱歉我沒說清楚, 系統是採用三層式的架構, N台Ap Server對應一台DB Server
確認使用者是否連線中或早已當機, 由Client端發出訊息, 由Ap Server去更新資料庫 , 同一時間, Ap Server
也去檢查資料庫當中有哪些連線是超過三分鐘都沒有更新的狀態
在下只是有這樣的初步構想, 若有更好更便利的做法那當然是更好
至於DB的連線數上限, 因為三層式架構應該無從得知有多少Client端吧

2.可以說明是什麼樣的程式會這樣?要幾個小時? DNA 資料庫 matchimg ?
Ans: 在下只是小小工程師, 公司的ERP系統, 無力修改所有的子系統與報表, 除了Stored Procedure & Trigger,
很多資料還是得透過Script的運算再運算, 資料庫雖然不算龐然大物, 但是幾十萬筆的資料交叉查詢後運算,
能夠縮短開啟時間的最佳化方式大多都測試運用了, 還是得需要一段時間才能開啟某些報表
正因為如此, 在等待報表開啟的同時, 並不能被系統判定為[當機中], 所以才會需要TTimer每三分鐘告訴DB,
目前這個Client還在執行中, 但測試結果是TTimer會受到TQuery的影響無法正確運作, 所以才想改用TThread
===================引 用 文 章===================
系統時間:2024-04-24 14:45:11
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!