2009-02-16 22 views
5

問:我正在尋找的是最典型的或最佳的做法是使用一個單獨的線程中印接受使用IdTCPClient數據路10Indy 10 IdTCPClient使用單獨的線程讀取數據?

背景:下面的代碼是一個爲了清晰起見,我試圖去除實際數據處理部分的樣本。 Thread的思想是接收所有數據(可變長度,頭部聲明剩餘的消息長度),然後解析它(這是HandleData過程的作用)並根據命令觸發事件處理程序。

的TIdIOHandlerSocket由主應用程序,它也將數據寫入到插座作爲和當需要傳遞給線程。

TScktReceiveThread = class(TThread) 
    private 
    { Private declarations } 
    procedure HandleData; 
    protected 
    procedure Execute; override; 
    public 
    FSocket: TIdIOHandlerSocket; 
    constructor Create(CreateSuspended: boolean); 
    end; 


procedure TScktReceiveThread.Execute; 
var 
    FixedHeader: TBytes; 
begin 
    Assert(FSocket <> nil, 'You must assign the connected socket to the receiving thread'); 
    SetLength(FixedHeader, 2); 
    while not Terminated do 
    begin 
     if not FSocket.Connected then 
     Suspend 
     else 
     begin 
      FSocket.CheckForDataOnSource(10); 
      if not FSocket.InputBufferIsEmpty then 
      begin 
      FSocket.ReadBytes(FixedHeader, SizeOf(FixedHeader), false); 
      // Removed the rest of the reading and parsing code for clarity 
      Synchronize(HandleData); 
      end; 
     end; 
    end; 
end; 

作爲前綴,我用另一個StackOverflow的問題與印的服務器組件涉及:「Delphi 2009, Indy 10, TIdTCPServer.OnExecute, how to grab all the bytes in the InputBuffer」得到一個什麼樣我到目前爲止的基礎。

感謝您的幫助!

回答

8

如果您希望避免施加的開銷通過爲每個線程類和每一個客戶端 - 服務器的數據交換,如

http://delphidicas.blogspot.com/2008/08/anonymous-methods-when-should-they-be.html

描述你可以創建一個能動的線程類我前幾天有同樣的問題,我只是給我寫一個類TMotileThreading它具有靜態函數,可以使用D2009的新匿名方法功能創建線程。看起來是這樣的:

type 
    TExecuteFunc = reference to procedure; 

    TMotileThreading = class 
    public 
    class procedure Execute (Func : TExecuteFunc); 
    class procedure ExecuteThenCall (Func : TExecuteFunc; ThenFunc : TExecuteFunc); 
    end; 

第二個過程讓我在你的情況下執行客戶端 - 服務器通信等,並做一些東西只要數據已經到達。關於匿名方法的好處是你可以使用調用上下文的局部變量。因此,通信看起來是這樣的:

var 
    NewData : String; 
begin 
    TMotileThreading.ExecuteThenCall (
    procedure 
    begin 
     NewData := IdTCPClient.IOHandler.Readln; 
    end, 
    procedure 
    begin 
     GUIUpdate (NewData); 
    end); 
end; 

的執行和ExecuteThenCall方法只需創建一個工作線程,FreeOnTerminate設置爲true,以簡化存儲管理,並在工作線程的執行和OnTerminate執行程序所提供的功能。

希望有所幫助。

編輯(按要求全面推行類TMotileThreading的)

type 
    TExecuteFunc = reference to procedure; 

    TMotileThreading = class 
    protected 
    constructor Create; 
    public 
    class procedure Execute (Func : TExecuteFunc); 
    class procedure ExecuteAndCall (Func : TExecuteFunc; OnTerminateFunc : TExecuteFunc; 
           SyncTerminateFunc : Boolean = False); 
    end; 

    TMotile = class (TThread) 
    private 
    ExecFunc    : TExecuteFunc; 
    TerminateHandler  : TExecuteFunc; 
    SyncTerminateHandler : Boolean; 
    public 
    constructor Create (Func : TExecuteFunc); overload; 
    constructor Create (Func : TExecuteFunc; OnTerminateFunc : TExecuteFunc; 
         SyncTerminateFunc : Boolean); overload; 
    procedure OnTerminateHandler (Sender : TObject); 
    procedure Execute; override; 
    end; 

implementation 

constructor TMotileThreading.Create; 
begin 
    Assert (False, 'Class TMotileThreading shouldn''t be used as an instance'); 
end; 

class procedure TMotileThreading.Execute (Func : TExecuteFunc); 
begin 
    TMotile.Create (Func); 
end; 

class procedure TMotileThreading.ExecuteAndCall (Func : TExecuteFunc; 
               OnTerminateFunc : TExecuteFunc; 
               SyncTerminateFunc : Boolean = False); 
begin 
    TMotile.Create (Func, OnTerminateFunc, SyncTerminateFunc); 
end; 

constructor TMotile.Create (Func : TExecuteFunc); 
begin 
    inherited Create (True); 
    ExecFunc := Func; 
    TerminateHandler := nil; 
    FreeOnTerminate := True; 
    Resume; 
end; 

constructor TMotile.Create (Func : TExecuteFunc; OnTerminateFunc : TExecuteFunc; 
          SyncTerminateFunc : Boolean); 
begin 
    inherited Create (True); 
    ExecFunc := Func; 
    TerminateHandler := OnTerminateFunc; 
    SyncTerminateHandler := SyncTerminateFunc; 
    OnTerminate := OnTerminateHandler; 
    FreeOnTerminate := True; 
    Resume; 
end; 

procedure TMotile.Execute; 
begin 
    ExecFunc; 
end; 

procedure TMotile.OnTerminateHandler (Sender : TObject); 
begin 
    if Assigned (TerminateHandler) then 
    if SyncTerminateHandler then 
     Synchronize (procedure 
        begin 
        TerminateHandler; 
        end) 
    else 
     TerminateHandler; 
end; 
+0

這是非常優雅的,但你有沒有在任何地方發佈完整的實現?我無法在您的文章中找到TMotileThreading類的完整實現。 – jamiei 2009-07-18 14:54:07

5

你在正確的軌道上。 Indy是打算這樣使用。它使用阻止套接字,所以ReadBytes調用不會返回,直到它被讀取請求。與非阻塞套接字相比,呼叫可能會提前返回,因此您可以輪詢或異步通知以確定請求何時已被填充。

印設計與插座對象有自己的線程(或纖維)的期望。對於想要將套接字組件拖放到其窗體和數據模塊並使用主GUI線程中的Indy組件的Indy,Indy帶有TIdAntifreeze,但如果可以避免,通常不是一個好主意。

由於您的線程不能沒有工作FSocket被分配,我勸你簡單地收到在類的構造函數值。如果未分配,則在構造函數中斷言。此外,它是一個錯誤創建你的線程非暫停,所以爲什麼甚至給出選項? (如果線程沒有被創建掛起,那麼它將開始運行,檢查是否分配了FSocket,並且由於創建線程還沒有分配到該字段而失敗。)

+0

是啊,你是對CreateSuspended絕對正確的。這是粘貼中的一個錯誤,我從默認線程複製了一個構造函數,因爲我的原始文件傳遞了其他我覺得會不必要地使代碼複雜化的東西!我很抱歉! – jamiei 2009-02-16 19:32:12