【问题标题】:How to run code in already running thread to safely send/recv data [TidTCPServer]如何在已经运行的线程中运行代码以安全地发送/接收数据 [TidTCPServer]
【发布时间】:2015-09-03 06:31:01
【问题描述】:

更新问题仍然存在。

是否可以在已经运行的线程中运行代码?例如: thread1 正在运行一些代码,我想从 thread1 中的 thread2 运行代码。

我想在 idTCPServer 线程中运行代码以向客户端发送一些数据

编辑: 经过研究,我的问题似乎是当客户端数据被接收或同时接收时,另一个线程正在尝试写入该套接字。

编辑:

procedure TMainFrm.UserSRVExecute(AContext: TIdContext);
var
 Command        : String;
 msSize         : Int64;
 ms             : TMemoryStream;
 decompressedMS : TMemoryStream;
 H              : TIdNotify;
begin
// Application.ProcessMessages;
 Command := AContext.Connection.Socket.ReadLn;
// messagebox(0,'snd','',$40);
 if logb then mainfrm.mconnections.Lines.Add(command + ' - BEGIN');

 if Command <> '' then   // keepalive 
  begin
  //Application.ProcessMessages;
   msSize         := AContext.Connection.Socket.ReadInt64;
   ms             := TMemoryStream.Create;
   decompressedMS := TMemoryStream.Create;
   try
    AContext.Connection.Socket.ReadStream(ms, msSize);
    ms.Position := 0;
    DecompressStream(MS,decompressedMS);
    decompressedMS.Position := 0;
    Client_ProcessData(AContext,Command,decompressedMS);
   finally
    ms.Free;
    decompressedMS.Free;
    if logb then mainfrm.mconnections.Lines.Add(command + ' - END');
   end;
  end;
end;


procedure Client_ProcessData(AContext: TIdContext; cmd : String; data : TMemoryStream);
var
 Hnd     : THandle;
 clData  : TStringArray;
 TmpStr1 : String;
 Tmp     : String;
 TN      : TIdNotify;

 Sync    : TMySync;
 I,I2    : Integer;
begin
 Hnd := AContext.Connection.Socket.Binding.Handle;

 if cmd = 'scr' then  // RECEIVE COMMAND TO SEND TO CLIENT TO RECEIVE DATA FROM CLIENT
  begin
   Tmp := StreamToString(data);
   {Sync := TMySync2.Create(True);
   try
    Sync.cmd := cmd;
    Sync.hnd := Hnd;
    Sync.tmp := TmpStr1;
    Sync.Resume;
   finally
    //Sync.Free;
   end;  }
   log('>>> CLFROMAS: '+IntToStr(HND)+':::'+cmd+':::');
 //  SendCMDToSocket(MainFrm.UserSRV,StrToInt(Trim(Tmp)),'scr'+IntToStr(Hnd));
   I2 := StrToInt(Trim(Tmp));
  for I := 0 to 100 do
  if USRVData[i].hnd = I2 then
   begin
   // cs.Acquire;
    USRVData[i].CTX.Connection.Socket.WriteLn('scr'+IntToStr(Hnd));  // PLACED ALL CONTEXTs IN GLOBAL VARIABLE + ALL SOCKET HANDLES. <--- HERE IS THE PROBLEM
   // cs.Release;
    Break;
   end;
 //  log('>>> CLFROMAS: '+IntToStr(HND)+':::'+cmd+':::'+streamtostring(data));
   Exit;
  end;

   if Copy(cmd,1,Length('scr4u')) = 'scr4u' then // RECEIVE DATA FROM CLIENT TO SEND IT TO ADMIN CLIENT REQUEST ABOVE
   begin
    if Length(cmd) > Length('scr4u') then
     begin
      Delete(cmd,1,Length('scr4u'));
      Data.Position := 0;
     { Sync := TMySync.Create;
      try
       Sync.cmd := cmd;
       Sync.hnd := Hnd;
       Sync.data := TMemoryStream.Create;
       Sync.data.CopyFrom(data,data.Size);
       Sync.data.Position := 0;
       Sync.DoNotify;
      finally
       Sync.data.Free;
       Sync.Free;
      end;  }
      SendStreamToSocket(MainFrm.UserSRV,strtoint(cmd),'scr4u',Data);

      log('>>>>> ADMIN: '+IntToStr(HND)+':::'+cmd+':::'{+streamtostring(data)});
     end else TmpStr1 := '';
    Exit;
   end;

   ...

更新

procedure TMainFrm.UserSRVExecute(AContext: TIdContext);
var
 Command        : String;
 msSize         : Int64;
 ms             : TMemoryStream;
 decompressedMS : TMemoryStream;
 H              : TIdNotify;
 I              : Integer;
 List, Messages : TStringList;
begin
  Messages := nil;
  try
   List := TMyContext(AContext).OutgoingMessages.Lock;
   try
    if List.Count > 0 then
     begin
      Messages := TStringList.Create;
      Messages.Assign(List);
      List.Clear;
     end;
   finally
    TMyContext(AContext).OutgoingMessages.Unlock;
   end;
   if Messages <> nil then
    begin
     for I := 0 to Messages.Count-1 do
      begin
       AContext.Connection.IOHandler.WriteLn(Messages.Strings[I]);
      end;
    end;
  finally
   Messages.Free;
  end;

  if AContext.Connection.IOHandler.InputBufferIsEmpty then
   begin
    AContext.Connection.IOHandler.CheckForDataOnSource(100);
    AContext.Connection.IOHandler.CheckForDisconnect;
    if AContext.Connection.IOHandler.InputBufferIsEmpty then
      Exit;
   end;

 Command := AContext.Connection.Socket.ReadLn;

 if logb then mainfrm.mconnections.Lines.Add(command + ' - BEGIN');

 if Command <> '' then
  begin
   msSize         := AContext.Connection.Socket.ReadInt64;
   ms             := TMemoryStream.Create;
   decompressedMS := TMemoryStream.Create;
   try
    AContext.Connection.Socket.ReadStream(ms, msSize);
    ms.Position := 0;
    DecompressStream(MS,decompressedMS);
    decompressedMS.Position := 0;
    Client_ProcessData(AContext,Command,decompressedMS);
   finally
    ms.Free;
    decompressedMS.Free;
    if logb then mainfrm.mconnections.Lines.Add(command + ' - END');
   end;
  end;
end;

【问题讨论】:

  • 你能解释一下你到底想做什么吗?目前还不是很清楚。您的线程可以有多个过程,并且在您发送一些信号以运行一个之后,它可以运行任何代码。或者你thread1可以启动thread2。或者 thread1 可以运行 thread2.Execute(或任何其他过程)。
  • 您需要您的线程进行合作和交流。让一个线程要求另一个线程做一些工作。
  • 我想在 idTCPServer 线程中运行代码向客户端发送一些数据
  • 请不要在评论中提问。请edit问题添加缺少的详细信息。
  • @user2200585:thread2 在哪里? IdTCPServer 对每个连接都有自己的线程,但您不必关心这一点。 IdTCPClients 将数据发送到 IdTCPServer,IdTCPServer 将数据发回。为什么需要另一个线程?您可能应该首先查看此类通信的演示。

标签: multithreading delphi thread-safety indy delphi-xe8


【解决方案1】:

是否可以在已经运行的线程中运行代码?例如:thread1 正在运行一些代码,我想在 thread1 中运行来自 thread2 的代码。

没有。 Thread1 需要显式编码以停止它当前正在执行的操作,执行其他操作,然后返回到之前执行的操作。 Thread2 所能做的就是向 Thread1 发出信号,让其尽早执行停止+继续操作。

我想在 idTCPServer 线程中运行代码以向客户端发送一些数据

您的TIdTCPServer.OnExecute 事件处理程序需要定期检查该数据并在可用时发送。

您可以使用TIdContext.Data 属性,或从TIdServerContext 派生自定义类并将其分配给TIdTCPServer.ContextClass 属性,为您的出站数据提供每个客户端线程安全的缓冲区。然后,您的 OnExecute 处理程序可以在需要时访问该缓冲区。

例如:

type
  TMyContext = class(TIdServerContext)
  public
    constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override;
    destructor Destroy; override;
    OutgoingMessages: TIdThreadSafeStringList;
  end;

constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
begin
  inherited;
  OutgoingMessages := TIdThreadSafeStringList.Create;
end;

destructor TMyContext.Destroy;
begin
  OutgoingMessages.Free;
  inherited;
end;

procedure TMyForm.FormCreate(Sender: TObject);
begin
  // this must be set before activating the server...
  IdTCPServer1.ContextClass := TMyContext;
end;

procedure TMyForm.IdTCPServer1Execute(AContext: TIdContext);
var
  List, Messages: TStringList;
begin
  // check for outgoing data...

  Messages := nil;
  try
    List := TMyContext(AContext).OutgoingMessages.LockList;
    try
      if List.Count > 0 then
      begin
        Messages := TStringList.Create;
        Messages.Assign(List);
        List.Clear;
      end;
    finally
      TMyContext(AContext).OutgoingMessages.UnlockList;
    end;
    if Messages <> nil then
    begin
      // send Messages using AContext.Connection.IOHandler as needed...
    end;
  finally
    Messages.Free;
  end;

  // check for incoming data...

  if AContext.Connection.IOHandler.InputBufferIsEmpty then
  begin
    AContext.Connection.IOHandler.CheckForDataOnSource(100);
    AContext.Connection.IOHandler.CheckForDisconnect;
    if AContext.Connection.IOHandler.InputBufferIsEmpty then
      Exit;
  end;

  // process incoming data as needed...
end;

procedure TForm1.SomeProcedure;
var
  List: TIdContextList;
  Context: TMyContext;
begin
  List := IdTCPServer1.Contexts.LockList;
  try
    Context := TMyContext(List[SomeIndex]);
    Context.OutgoingMessages.Add('something');
  finally
    IdTCPServer1.Contexts.UnlockList;
  end;
end;

【讨论】:

  • 让一个线程从一个套接字读取而另一个线程写入到同一个套接字是完全安全的。不安全的是让两个或多个线程同时从同一个套接字读取,或者两个或多个线程同时写入到同一个套接字。您必须同步线程以确保一个线程不会窃取另一个线程的传入数据,并且一个线程的传出数据不会与另一个线程的传出数据重叠。
  • Edit your question 显示您的代码。
  • 您显示的代码不是线程安全的。您正在访问 UI 控件而不与主 UI 线程同步。您正在访问套接字连接,但没有保护它们免受多个线程的并发访问。您没有应用我在上面的回答中概述的任何概念。你需要这样做。
  • 是的,但这可以处理 100 个客户吗?或者更多?会不会很慢?所有“日志”程序都将是线程安全的或被删除,它不是永远的,我已经添加它们来跟踪问题..
  • 是的,我所描述的可以处理TIdTCPServer 可以接受的尽可能多的客户
猜你喜欢
  • 1970-01-01
  • 2016-01-23
  • 2012-01-20
  • 2010-09-25
  • 1970-01-01
  • 2012-04-05
  • 1970-01-01
  • 1970-01-01
  • 2019-12-29
相关资源
最近更新 更多