2017-05-29 72 views
-2

我刚刚开始使用线程在Delphi 2009中使用onexecute事件,indy IdTCPServer1。我写了一个非常基本的应用程序进行测试,并在退出时遇到访问冲突。该应用程序运行良好,并做我想要的一切,但我认为我离开“线程运行”退出。我没有线程的经验,所以任何帮助,将不胜感激。德尔福2009,IdTCPServer1访问冲突退出

继承人我的代码

unit FT_Communicator_pas; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, StdCtrls, ScktComp, IdContext, IdTCPServer, 
    INIFiles, ExtCtrls, ComCtrls, adscnnct, 
    DB, adsdata, adsfunc, adstable, Wwdatsrc, Grids, Wwdbigrd, Wwdbgrid, 
    IdBaseComponent, IdComponent, IdCustomTCPServer; 


type 
    TfrmMain = class(TForm) 
    IdTCPServer1: TIdTCPServer; 
    PgMain: TPageControl; 
    TsMain: TTabSheet; 
    tsConfig: TTabSheet; 
    Label1: TLabel; 
    Label2: TLabel; 
    txtServer: TEdit; 
    txtPort: TEdit; 
    Panel1: TPanel; 
    Panel3: TPanel; 
    tsLog: TTabSheet; 
    mnolog: TMemo; 
    Button1: TButton; 
    Button3: TButton; 
    procedure IdTCPServer1Connect(AContext: TIdContext); 
    procedure IdTCPServer1Execute(AContext: TIdContext); 
    procedure Button3Click(Sender: TObject); 
    procedure IdTCPServer1Disconnect(AContext: TIdContext); 
    procedure Logit(const Logstr: String); 
    procedure FormShow(Sender: TObject); 
    procedure Button1Click(Sender: TObject); 
    private 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 



var 
    frmMain: TfrmMain; 

implementation 
{$R *.dfm} 

procedure TfrmMain.Button1Click(Sender: TObject); 
begin 


    IdTCPServer1.Active:=FALSE; 

    application.Terminate; 
end; 

procedure TfrmMain.Button3Click(Sender: TObject); 
begin 
    IdTCPServer1.Active:=true; 
end; 

procedure TfrmMain.FormShow(Sender: TObject); 
begin 
    PgMain.ActivePage:=tsMain; 
    EnableMenuItem(GetSystemMenu(handle, False),SC_CLOSE, MF_BYCOMMAND or MF_GRAYED); 
end; 


procedure TfrmMain.IdTCPServer1Connect(AContext: TIdContext); 
begin 
    mnoLog.lines.Add ('Connected from: ' + AContext.Connection.Socket.Binding.PeerIP); 
end; 

procedure TfrmMain.IdTCPServer1Disconnect(AContext: TIdContext); 
begin 
    mnoLog.lines.Add ('Disconnected from: ' + AContext.Connection.Socket.Binding.PeerIP); 
end; 

procedure TfrmMain.IdTCPServer1Execute(AContext: TIdContext); 
var 
    myReadln,mySendln,sqlqry:string; 
begin 


    sleep(10); 

    myReadln:=AContext.Connection.IOHandler.ReadLn(); 
    mnolog.Lines.Add(AContext.Connection.Socket.Binding.PeerIP + '>' + myReadln); 
    mySendln:= AContext.Connection.Socket.Binding.PeerIP + ' Sent me ' + myReadln; 
    AContext.Connection.IOHandler.WriteLn(mySendln); 

    try 
    except 
     on E:Exception do 
     begin 
      logit('Error occured During execute function ' + #13#10 + e.message); 
     end; 
    end; 

end; 

procedure TfrmMain.logit(const logstr:String); 
var 
    curdate,Curtime:string; 
    StrGUID:string; 
begin 
    StrGUID:=FormatDateTime('YYYYMMDDHHnnsszzz', Now())+'_ '; 
    mnolog.lines.add(StrGUID +logstr); 
end; 

end. 
+0

你的try/except块具有里面什么都没有。因此,它无法捕捉任何东西。它有什么意义? –

+0

你能提供[mcve]吗?我想还有很多事情要做,重要的.dfm文件中的设置。大概是通信的另一端。不要提供GUI程序。试图让它在这里运行是一件很痛苦的事情。以控制台应用程序的形式提供干净的[mcve]。 –

+0

我是自学的,所以我甚至不知道如何做一个控制台应用程序,但我会给它一个去。此外,对于故障排除,我删除了试验中的编码,除非我在某处读取尝试/ Execute事件除外,可以防止控制处理它自己的异常。非常感谢回复 – John

回答

2

TIdTCPServer事件处理程序包含在其中不安全的代码。

TIdTCPServer是一个多线程组件,它的事件在工作线程的上下文中被触发。但是,您直接访问VCL UI控件(mnoLog)而不与主UI线程同步。当你不同步时会发生坏事,因为VCL不是线程安全的。从工作线程访问UI时,必须正确同步。

当从主UI线程停用TIdTCPServer时避免执行同步同步也很重要,因为这会导致死锁。改为使用异步同步。

尝试更多的东西像下面这样:

procedure TfrmMain.IdTCPServer1Connect(AContext: TIdContext); 
begin 
    Logit('Connected from: ' + AContext.Connection.Socket.Binding.PeerIP); 
end; 

procedure TfrmMain.IdTCPServer1Disconnect(AContext: TIdContext); 
begin 
    Logit('Disconnected from: ' + AContext.Connection.Socket.Binding.PeerIP); 
end; 

procedure TfrmMain.IdTCPServer1Execute(AContext: TIdContext); 
var 
    myReadln, mySendln, peerIP: string; 
begin 
    myReadln := AContext.Connection.IOHandler.ReadLn(); 
    peerIP := AContext.Connection.Socket.Binding.PeerIP; 
    Logit(peerIP + '>' + myReadln); 
    mySendln := peerIP + ' Sent me ' + myReadln; 
    AContext.Connection.IOHandler.WriteLn(mySendln); 
end; 

procedure TfrmMain.IdTCPServer1Exception(AContext: TIdContext; AException: Exception); 
begin 
    if not (AException is EIdConnClosedGracefully) then 
    Logit('Error occured. ' + AException.Message); 
end; 

procedure TfrmMain.Logit(const Logstr: String); 
var 
    Str: string; 
begin 
    Str := Trim(Logstr); 
    TThread.Queue(nil, 
    procedure 
    begin 
     mnolog.Lines.Add(FormatDateTime('YYYYMMDDHHnnsszzz', Now()) + ': ' + Str); 
    end 
); 
end; 
+0

谢谢Remy,'EIdConnClosedGracefully'在哪里定义?如果这是一个简单的答案,我自学非常抱歉。 – John

+0

好吧,我想我明白了。我正在从一个线程访问gui对象。我如何安全地做这件事。有没有一个简单的资源/教程,你可以推荐,这将有助于我理解多线程以及如何使用它编码?我非常感谢帮助。 – John

+0

好的,知道它的工作。非常感谢你的帮助 – John