2012-01-25 26 views
2

是否有掛鉤到dbx用戶會話的WndProc的方法?掛鉤到dbx DataSnap用戶會話的消息循環

背景: dbx DataSnap使用Indy組件進行TCP通信。最簡單的形式是,DataSnap服務器是一個接受連接的Indy TCP服務器。建立連接時,Indy爲該連接創建一個線程,該線程處理該連接的所有請求。

這些用戶連接都消耗資源。對於具有幾百個同時連接的服務器,這些資源可能很昂貴。許多資源可以合併,但我不希望在每次需要時總是獲取和釋放資源。

相反,我想實現一個空閒計時器。線程完成資源後,計時器將啓動。如果線程在計時器過去之前訪問資源,資源仍然會被「分配」給該線程。但是如果定時器在下次訪問之前流逝,資源將被釋放回池中。線程下一次需要資源時,將從池中獲取另一個資源。

我還沒有找到辦法做到這一點。我試過使用SetTimer,但我的計時器回調從不會觸發。我認爲這是因爲Indy的線程WndProc沒有調度WM_TIMER。我無法控制此線程的「執行循環」,因此無法輕鬆檢查是否發生了事件。實際上,除非線程處理用戶請求,否則我的代碼不會執行此線程。事實上,我想讓代碼在任何用戶請求之外執行。

同樣讚賞解決原始問題或替代方法的建議。

+0

雷米勒博別處指出,Indy的線程沒有消息循環。我以前曾嘗試創建一個消息循環並在用戶線程中實現我自己的WndProc,但是我的WndProc從未收到過信息。 SetTimer和傳統的TTimer都不支持我的消息循環。 (不考慮線程安全問題,已經解決了這些問題。)我在很多其他應用程序的許多線程中創建了消息循環。但絕不會在DataSnap或Indy TCP服務器中。 –

+0

我應該提到我正在使用Delphi XE和DSTCP傳輸。在另一個論壇上,Mat DeLong建議使用TDSSessionManager.Instance.AddSessionEvent。不幸的是,對於Delphi XE中的TCP連接,TDSSessionManager效果不佳。 XE2中已經解決了一些嚴重問題。 –

+0

我相信你在這裏做任何事情的機會都需要XE2,它有一些新的功能,可能會減少你的需要,甚至擔心像這個閒置計時器這樣的低級別攻擊。 –

回答

1

我們嘗試使用TCP連接(不使用HTTP傳輸,因此沒有SessionManager)在用戶線程之間共享資源,但遇到了各種各樣的問題。最終我們放棄使用單獨的用戶線程(設置LifeCycle := TDSLifeCycle.Server)和ServerContainerUnit創建了自己的FResourcePoolFUserList(均爲TThreadList)。它只需要1天的時間來實施,而且工作得很好。

這裏是我們所做的一個簡化版本:

TResource = class 
    SomeResource: TSomeType; 
    UserCount: Integer; 
    LastSeen: TDateTime; 
end; 

當用戶連接,我們檢查FResourcePoolTResource用戶的需求。如果存在,我們增加資源的UserCount屬性。當用戶完成後,我們遞減UserCount屬性並設置LastSeen。我們有一個TTimer,每60秒觸發一次,釋放大於60秒的UserCount = 0LastSeen的資源。

FUserList非常相似。如果一個用戶幾個小時都沒有被看到,我們假設他們的連接被切斷了(因爲我們的客戶端應用程序在用戶閒置了90分鐘時會自動斷開連接),所以我們以編程方式斷開服務器端的用戶連接,這也減少了他們對每種資源的使用。當然,這意味着我們必須自己創建一個會話變量(例如,CreateGUID();),並在第一次連接時將其傳遞給客戶端。客戶端會將每個請求的會話ID傳遞迴服務器,以便我們知道哪些記錄是他們的。雖然這是使用用戶線程的而不是的缺點,但它很容易管理。

+0

感謝您的建議。我並不是答案,因爲它不能回答我的問題。但我很欣賞這些反饋。在製作過程中,我們看到了160多位同時在線的用戶,並且在任何特定時刻都有相當多的人在用。 「每用戶一個線程」模式對我們來說效果很好,並不是我想要失去的東西。當DataSnap已經做得很好時,我也不想實現自己的線程處理。 –

1

詹姆斯L也許釘了它。由於Indy線程沒有消息循環,所以您必須依賴另一種機制 - 像只讀線程本地屬性(例如UserCount和/或LastSeem在他的示例中) - 並使用主線程服務器根據某些規則運行TTimer以釋放資源。

編輯:另一個想法是創建一個共同的數據結構(下面的例子),每次線程完成其'工作時更新。

警告:從腦海編碼只......它可能無法編譯... ;-)

例子:

TThreadStatus = (tsDoingMyJob, tsFinished); 

TThreadStatusInfo = class 
private 
    fTStatus : TThreadStatus; 
    fDTFinished : TDateTime; 
    procedure SetThreadStatus(value: TThreadStatus); 
public 
    property ThreadStatus: TThreadStatus read fTStatus write SetStatus; 
    property FinishedTime: TDateTime read fDTFinished; 
    procedure FinishJob ; 
    procedure DoJob; 
end 

procedure TThreadStatusInfo.SetThreadStatus(value : TThreadStatus) 
begin 
    fTStatus = value; 
    case fTStatus of 
    tsDoingMyJob : 
     fDTFinished = TDateTime(0); 
    tsFinished: 
     fDTFinished = Now; 
    end; 
end; 

procedure TThreadStatusInfo.FinishJob; 
begin 
    ThreadStatus := tsFinished; 
end; 

procedure TThreadStatusInfo.DoJob; 
begin 
    ThreadStatus := tsDoingMyJob; 
end; 

把它放在一個列表(任何列表類,你像),並確保每個線程都與 相關聯,並在該列表中包含一個索引。只有當您不再使用該線程數量(縮小列表)時才從列表中刪除項目。添加一個項目,當你創建一個新的線程 (例如,你有4個線程,現在你需要第5個,你創建一個新項目主線程)。

因爲每個線程都在名單上的索引,你並不需要封裝這個寫(在TCriticalSection的 電話對T 。

您可以閱讀這份名單沒有麻煩,在使用TTimer主線程檢查 每個線程的狀態。既然你有每個線程的完成時間 的時間,你可以計算超時。

+0

我很抱歉downvoting,但我不知道你爲什麼發佈這個答案。相反,您可以評論或提出James L的回答。 –

+0

我打算增加另一個想法,但我得到了一個優先級轉移,使我做了一個不完整的答案。 –

+0

你是最善良的downvoters之一...有些人似乎只是爲了運動downvote ... –