全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:1995
推到 Plurk!
推到 Facebook!

利用TIdTCPServer接受資料並存入資料庫中發生的問題

尚未結案
smallma
一般會員


發表:8
回覆:11
積分:3
註冊:2003-03-05

發送簡訊給我
#1 引用回覆 回覆 發表時間:2004-04-21 02:05:45 IP:211.75.xxx.xxx 未訂閱
小弟的程式是要利用TIdTCPServer處理client端傳來的資料(有可能多筆),並insert入資料庫,程式在測試的時候run的很順,但在實際上線的情況下程式只要run個一整天就會出現各種奇怪的錯訊息並掛掉了,我發現在工作管理員中,這個程式所佔用的記憶體會隨程式執行的時間以及它處理的需求,慢慢的增加,直到最後就掛了,我懷疑是TIdTCPServer的OnExecute的thread中,一定有部份的記憶體沒有在thread結束時釋放,而造成每次OnExecute都會造成程式佔用記憶體的增加,這是我推論有可能發生問題的點,到最後的結果就是程式掛掉,希望哪位大大能幫幫我解決這個問題,程式的片斷如下: procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread); var recstr,sqlstr: string; A,B,C,D: string; Query1: TADOQuery; begin with AThread.Connection do begin //接受傳來的字串 recstr := ReadLn; //由recstr取出第一筆資料,每個欄位以空格做間隔 A := fetch(recstr,' '); B := fetch(recstr,' '); C := fetch(recstr,' '); D := fetch(recstr,' '); //設定sql指令的第一筆資料 sqlstr := 'insert into ABCD(A,B,C,D) values (' QuotedStr(A) ',' QuotedStr(B) ',' QuotedStr(C) ',' QuotedStr(D) ')'; //如果資料不止一筆,讀資料並加入sql到讀完為止 while length(recstr) > 0 then begin A := fetch(recstr,' '); B := fetch(recstr,' '); C := fetch(recstr,' '); D := fetch(recstr,' '); sqlstr := sqlstr ',(' QuotedStr(A) ',' QuotedStr(B) ',' QuotedStr(C) ',' QuotedStr(D) ')'; end; //設定Query元件 Query1 := TADOQuery.Create(self); Query1.Connection := ADOConnection1; Query1.SQL.Clear; Query1.SQL.Add(sqlstr); //執行sql指令加入資料庫並free query try Query1.ExecSQL; Query1.Free; except on e: exception do begin Query1.Free; end; end; end; end; ps:使用的資料庫為mysql,以ado元件連結delphi 6
Chance36
版主


發表:31
回覆:1033
積分:792
註冊:2002-12-31

發送簡訊給我
#2 引用回覆 回覆 發表時間:2004-04-21 19:56:58 IP:211.20.xxx.xxx 未訂閱
smallma 你好    請改成如下程式
  .......
  //執行sql指令加入資料庫並free query
  try
    Query1.ExecSQL;
  except
    on e: exception do begin
      .......
    end;
  end;
  If Assigned(Query1) Then 
    Query1.Free;
end;    若還有問題,你可以試著把Try...Except的包含範圍拉大一點到
Query1 := TADOQuery.Create(self);之前如下      Try
    Query1 := TADOQuery.Create(self);
    ....
    ....
    ....
    Query1.ExecSQL;
  Except
    ......
  End;
  If Assigned(Query1) Then 
    Query1.Free;
 
smallma
一般會員


發表:8
回覆:11
積分:3
註冊:2003-03-05

發送簡訊給我
#3 引用回覆 回覆 發表時間:2004-04-21 21:18:06 IP:211.75.xxx.xxx 未訂閱
非常謝謝您的回應,我會加上並開始測試,不過這種東西要測比較長的時間,希望能有好的結果,謝謝
ha0009
版主


發表:16
回覆:507
積分:639
註冊:2002-03-16

發送簡訊給我
#4 引用回覆 回覆 發表時間:2004-04-21 21:51:46 IP:61.56.xxx.xxx 未訂閱
你好:
    建議改為如下的範例,try .. finally 會確保一定釋放你的物件,而
使用 FreeAdNil 則可以在釋放物件的同時把物件指標設為 Nil    Query1 := TADOQuery.Create(self);
try
  Query1.Connection := ADOConnection1;      Query1.SQL.Clear;
  Query1.SQL.Add(sqlstr);
finally
  FreeAndNil (Query1);
end;   
smallma
一般會員


發表:8
回覆:11
積分:3
註冊:2003-03-05

發送簡訊給我
#5 引用回覆 回覆 發表時間:2004-04-22 01:37:09 IP:211.75.xxx.xxx 未訂閱
目前的測試結果: (1)在thread的最後加入 if Assigned(Query1) Then FreeandNil(Query1); 這個方法經過5個小時的測試,佔用記憶體的情況有小幅改善(增加的速度變小),可是依照以往的經驗,大約這支程式會在約72小時後掛掉。 (2)除了(1)的方法之外我參考了之前的文章,在程式中放一個timer,每十分鐘做一次以下的指令: SetProcessWorkingSetSize(GetCurrentProcess,$FFFFFF,$FFFFFF); Sleep(100); 這對於這個程式的問題有明顯的改善,雖然不能完全的達到在一段時間後佔用記憶體的狀況完全不增加,但是已經可以接受,雖然測試時間不夠,不過這支程式應該可以穩定工作120小時以上。 (3)除了加上(2)的timer之外,所有的thread全部使用同一個adoquery,先宣告一個全域變數QueryIsUsing為TCriticalSection,並將thread的onexecte中有關使用query的部份程式碼改寫如下: QueryIsUsing.Acquire; Query1.Close; Query1.SQL.Clear; Query1.SQL.Add('XXXXXXXX') <--執行的指令 Query1.ExecSQL; QueryIsUsing.Release; 這樣做的好處是完全沒有create、以及free adoquery的動作,完全就是一個adoquery用到底,理論上,應該不可能造成程式佔用記憶體上昇的問題,可是實際上在使用的時候,佔用記憶體的情況仍然發生,甚至效果還比不上(2) 以下是我的結論: 利用query在新增資料的時候,也許是無可避免的,一定會有一些記憶體的片斷沒有釋放,目前我測試效果最好的方式是使用SetProcessWorkingSetSize重新調整,不過有了它可以大幅度的延長程式執行的時間,不過我想這也不是一個100分的做法,不知道大家有什麼意見,希望能多多提出來討論~~~
smallma
一般會員


發表:8
回覆:11
積分:3
註冊:2003-03-05

發送簡訊給我
#6 引用回覆 回覆 發表時間:2004-04-22 01:55:18 IP:211.75.xxx.xxx 未訂閱
補充一下另一個新的想法: 如果佔用記憶體的情況不能100%避免,另一個可能的想法就是將這支程式設定為一段時間後(也許是72小時或更久),就會自動關閉,另外由一支附帶的程式來偵測,如果主要程式被關閉,附帶程式就自動開啟一個全新的主要程式,這是一個想法。 不過這個想法會有一個問題,就是這個有可能同時有很多thread在執行的程式,如果自己呼叫self.close;常常會由於有些thread還沒有執行完,會造成出現錯誤訊息(accessviolation),或是"這個程式已經沒有回應"的messagebox,這樣的結果不但是主要程式當掉,連那個附帶的程式偵測也會因為主程式沒有"完全結束",而沒有作用,不知道有沒有大大知道怎麼樣避免這個情況呢???
Chance36
版主


發表:31
回覆:1033
積分:792
註冊:2002-12-31

發送簡訊給我
#7 引用回覆 回覆 發表時間:2004-04-22 02:12:53 IP:203.204.xxx.xxx 未訂閱
smallma 你好 使用一個query固然是省記憶體,但要注意執行緒同步的問題,會不會某個連線正在使用該Query時同時另一連線也要用而造成競爭的現象。 我倒建議使用LIst來管理連線,當OnConnect時則將該AThread加入ThreadList,而在DisConnect時將該AThread移除,同時使用另一QueryList記錄已建立的AdoQuery,當連線建立時除了加入ThreadList的管理之外,同時檢QueryList.Count是否>=ThreadList.Count,若不是則動態建加Query並加入QueryList中加以管理,所以此時每個AThread就有一個專用的Query可用,如此才不會打架,至於某個連線斷線時,只要將該AThread從ThreadList中移除即可,QueryList還是保持不要釋放。
microbean
初階會員


發表:1
回覆:43
積分:38
註冊:2004-04-09

發送簡訊給我
#8 引用回覆 回覆 發表時間:2004-04-22 10:33:20 IP:210.58.xxx.xxx 未訂閱
對於 Memory Leak 的問題 , 很有興趣 , 我曾遇過在 MySql 上面使用 MySql ODBC , 發生嚴重的 Memory Leak 問題, 所以對於這個問題, 提供一些想法來追蹤 首先, 我假設 smallma 的程式語法都正確 ,也有正確的下釋放的指令 . 這樣可能會發生的 memory leak 來源 , 推斷有三個來源 1. MySQL ODBC 2. TAdoQuery 3. Indy 這三個裡面 , 我先排除 TAdoQuery, 機率比較小 . 接著 MySQL ODBC 是否有 Memory leak 的 bug , 我曾找過資料 都沒有證實, 但是 原則上 , 建議可以先更新到最新的版本 , 或者反向操作 , 先用 Access ODBC 取代 MySQL ODBC , 試試. Indy 是否有 Memory Leak 呢 ? 假設 TCPServer 是一個 multi thread 的元件 , 可以觀察 工作管理員的 執行緒數目 , 以及 記憶體的成長情形 原則上, 我們可以假設 一個 Connection 佔用 一個 thread 所以當 client 斷線時 , 這個 thread 以及佔用的記憶體 應該被釋放 . thread 的數目沒有因斷線而減少 , 那表示 , indy 的 thread 控制出了問題. 如果是這樣 , 就要從 indy thread 如何正確釋放著手. 另外 , 可以用 debug 的 trace 方法 , 針對 ServerExecute 的 每一行逐行去執行 , 並且觀察記憶體以及 thread 數目的變化 ,在我們預期應該釋放的地方 , 記憶體應該產生變化 , 應該減小. 以上 提供參考. _= 沒事就 winsock 一下 =_
smallma
一般會員


發表:8
回覆:11
積分:3
註冊:2003-03-05

發送簡訊給我
#9 引用回覆 回覆 發表時間:2004-04-22 12:12:30 IP:211.75.xxx.xxx 未訂閱
有關 microbean 提出的寶貴意見,我非常的感謝,我在這裡也針對他提出的三點提供一些我的經驗做為大家的參考: (1)MySql ODBC:這是最有可能,也是我到目前為止認為最大的嫌疑犯,不過要解決這個問題,也不知道從何下手 (2)TAdoQuery:我想應該不會是它,因為我在前面已經提過了,我利用TCriticalSection變數的Acquire和Relese來控制,讓所有的thread都只使用一個TAdoQuery,但是Memory Leak的情形完全沒有改善;順便提一下,我也試著使用過BDE的TQuery來做相同的實驗,結果是一樣的。 (3)Indy:這個也不可能,因為相同的程式我有另外一支也在跑,那一支負責的是查詢(select),而那支程式我可以連續跑二個星期以上,完全都不會有任何Memory Leak的情形,同樣的程式,同樣的語法,只是一個是查詢(query.open),一個是新增(query.execsql),就有如此大的差別,而這二個程式在使用indy方面的結構是完全相同的,所以可以排除indy的因素。 經由消去法(好像在辦案),(1)應該就是我們的犯人,可是要怎麼解決呢???
microbean
初階會員


發表:1
回覆:43
積分:38
註冊:2004-04-09

發送簡訊給我
#10 引用回覆 回覆 發表時間:2004-04-22 14:09:24 IP:210.58.xxx.xxx 未訂閱
smallma 兄 你可以在 access or MSSQL 上面測試看看嗎 ?? 也就是原本走 MySQL 的 odbc driver 現在走 access or MSSQL ... 就是把 db 換一下看看.... 如果真的證實是 my SQL ODBC driver 再來想辦法 .. 我之前的情形 , 我是這樣解的 由於我的 mysql 是在 unix 上 , 所以我在 apache 上面寫了一個 php 的接口程式 , 然後 我的 indy 就單純的使用 idHttp 去送 query 給 php . 這樣就解了 , 但是...安全性以及效能是需要在考量的.. 發表人 - microbean 於 2004/04/22 14:21:15 發表人 - microbean 於 2004/04/22 14:22:03
smallma
一般會員


發表:8
回覆:11
積分:3
註冊:2003-03-05

發送簡訊給我
#11 引用回覆 回覆 發表時間:2004-04-22 18:08:20 IP:211.75.xxx.xxx 未訂閱
microbean 兄 我明白您的意思了,也許換driver是一個根本的辦法,我曾經用過dbexpress,可是結果更慘,程式的穩定度大幅降低,而目前的3.51好像是唯一的選擇,我先用前面各位大大提到的方法試一下,看能夠做到什麼地步,不過因為這種測試需要很長的時間,所以可能這一二天不會有消息。 目前所想到的解法: (1)控制程式每隔個幾天自動重新開啟一次,不過我之前提過,這有風險 (2)換driver,像microbean兄所使用的方式,利用其他的driver(像php)來做
smallma
一般會員


發表:8
回覆:11
積分:3
註冊:2003-03-05

發送簡訊給我
#12 引用回覆 回覆 發表時間:2004-05-01 08:57:32 IP:211.75.xxx.xxx 未訂閱
謝謝大家的幫忙,我把這個問題最終發現的關鍵和大家報告一下: 很奇怪的,不知道什麼原因,只要在TIdTCPServer的OnExecute的thread中有執行query的insert的動作,在程式執行久了之後,我發現一個重要的關鍵,就是該程式的執行會愈來愈多,進而造成Memory Leak,所以抱著嘗試的心理試了一行指令,在OnExecute的最後加上一行: procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread); var .... begin with AThread.Connection do begin ....... end; AThread.Terminate; end; 結果出奇的好,再將之前大家討論的部份加上去,幾乎可以說是一個完美的結局,只是不知道它的原理到底是怎麼樣,還希望如果有高手在看的話,能給我們一點指示,謝謝大家
microbean
初階會員


發表:1
回覆:43
積分:38
註冊:2004-04-09

發送簡訊給我
#13 引用回覆 回覆 發表時間:2004-05-05 10:48:24 IP:210.58.xxx.xxx 未訂閱
這樣看來 , 當 client 斷線時 , Server 可能未處理 disconnect . 或者有處理 , 但是裡面仍有 thread 處在 pending 的狀況 . 這可以用 debug mode , 去 trace client 斷線以後的所有程式 (不只有要看自己寫的) , 推斷一定有一個地方 , pending 了. 把 thread 直接 teminate 掉 , 當然上面的資源就會隨之解決. 更好奇的是說 , 如果不用 indy 呢 ?? 是否就可以了.. (因為我常常遇到 indy 莫名的問題 , 我懷疑這是 indy 架構有問題) _= 沒事就 winsock 一下 =_
smallma
一般會員


發表:8
回覆:11
積分:3
註冊:2003-03-05

發送簡訊給我
#14 引用回覆 回覆 發表時間:2004-05-07 11:03:33 IP:211.75.xxx.xxx 未訂閱
跟大家報告一下,非常奇怪的,這個問題只有在insert的時候才會發生,delete及select的情況下是不會發生的。 我想這可能是bug吧~~也或者是我們使用上的錯誤(不能在thread中宣告並使用query???),不過總之這是一個可以接受的方案~~ 不用indy的話,好像也不知道要用什麼,呵呵
jeffreck
高階會員


發表:247
回覆:340
積分:197
註冊:2003-01-23

發送簡訊給我
#15 引用回覆 回覆 發表時間:2004-07-15 08:04:59 IP:61.218.xxx.xxx 未訂閱
引言: 跟大家報告一下,非常奇怪的,這個問題只有在insert的時候才會發生,delete及select的情況下是不會發生的。 我想這可能是bug吧~~也或者是我們使用上的錯誤(不能在thread中宣告並使用query???),不過總之這是一個可以接受的方案~~ 不用indy的話,好像也不知道要用什麼,呵呵
TRemoteDataModule try try see
Ktop_Robot
站務副站長


發表:0
回覆:3511
積分:0
註冊:2007-04-17

發送簡訊給我
#16 引用回覆 回覆 發表時間:2007-04-26 13:51:38 IP:000.000.xxx.xxx 未訂閱
提問者您好:


以上回應是否已得到滿意的答覆?


若已得到滿意的答覆,請在一週內結案,否則請在一週內回覆還有什麼未盡事宜,不然,
將由版主(尚無版主之區域將由副站長或站長)自由心證,選擇較合適之解答予以結案處理,
被選上之答題者同樣會有加分獎勵同時發問者將受到扣 1 分的處分。不便之處,請見諒。


有問有答有結案,才能有良性的互動,良好的討論環境需要大家共同維護,感謝您的配合。

------
我是機器人,我不接受簡訊.
系統時間:2024-05-18 11:54:50
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!