gpt4 book ai didi

multithreading - 带有计时器事件和其他多线程客户端事件的 Delphi tIdTCPClient

转载 作者:行者123 更新时间:2023-12-03 19:41:56 26 4
gpt4 key购买 nike

我们有一个使用 INDY 的 Delphi 客户端服务器应用程序。客户端与服务器的单个 tIdTCPClient 连接是多线程的。客户端“理论上”是一个单线程。但实际上客户端上有多个线程,这就是我的问题所在。例如,考虑一个每分钟触发一次以从服务器获取数据的计时器。并考虑当用户在此计时器事件的同时运行命令时会发生什么。事实上,我的问题是由我们的“报表生成器”报表工具引起的,它(令人讨厌地)坚持加载报表的每一页,这需要一段时间。该报告运行我们的“特殊”数据集,该数据集具有一次传输批量记录的缓存机制(因此多次调用服务器以获取所有数据)。同时,如果用户同时做其他事情,我们似乎得到了交叉数据。用户似乎取回了用于报告的数据。

顺便说一句,这个错误非常罕见,但对于一个拥有世界上最慢互联网的特定客户来说,这种情况要少得多(幸运的是,我现在有一个测试环境)。

所以在客户端我有这样的代码......

procedure DoCommand(MyIdTCPClient:tIdTCPClient; var DATA:tMemoryStream);
var
Buffer: TBytes;
DataSize: Integer;
CommsVerTest: String;
begin
//Write Data
MyIdTCPClient.IOHandler.Write(DATA.Size);
MyIdTCPClient.IOHandler.Write(RawToBytes(Data.Memory^,DataSize));

//Read back 6 bytes CommsVerTest should always be the same (ie ABC123)
SetLength(Buffer,0); //Clear out buffer
MyIdTCPClient.IOHandler.ReadBytes(Buffer,6);
CommsVerTest:=BytesToString(Buffer);
if CommsVerTest<>'ABC123' then
raise exception.create('Invalid Comms'); //It bugs out here in rare cases

//Get Result Data Back from Server
DataSize:=MyIdTCPClient.IOHandler.ReadLongInt;
Data.SetSize(DataSize); //Report thread is stuck here
MyIdTCPClient.IOHandler.ReadBytes(Buffer,DataSize);
end;

现在,当我调试它时,我可以确认当此过程中间有两个线程时它会出错。主线程在异常处停止。并且报告线程在同一过程中被卡在其他地方。

所以,在我看来,我需要使线程上面的过程安全。
我的意思是,如果用户想要做某事,他们只需要等到报告线程完成。

Arrrgh,我认为我的客户端应用程序是单线程的,用于向服务器发送数据!

我认为使用 TThread 是行不通的——因为我无法访问 Report Builder 中的线程。我想我需要一个 tCriticalSection。

我想我需要制作应用程序,以便一次只能由一个线程运行上述过程。其他线程必须等待。

有人请帮助语法。

最佳答案

TIdIOHandlerWrite()Read...()发送/接收过载TStream数据:

procedure Write(AStream: TStream; ASize: TIdStreamSize = 0; AWriteByteCount: Boolean = False); overload; virtual;
procedure ReadStream(AStream: TStream; AByteCount: TIdStreamSize = -1; AReadUntilDisconnect: Boolean = False); virtual;

您不需要复制 TMemoryStream内容到中间 TIdBytes在发送之前,或以 TIdBytes 的形式接收在将其复制回 TStream 之前。实际上,您显示的代码中没有任何内容需要使用 TIdBytes直接:
procedure DoCommand(MyIdTCPClient: TIdTCPClient; var DATA: TMemoryStream);
var
CommsVerTest: String;
begin
//Write Data
MyIdTCPClient.IOHandler.Write(DATA, 0, True);

//Read back 6 bytes CommsVerTest should always be the same (ie ABC123)
CommsVerTest := MyIdTCPClient.IOHandler.ReadString(6);
if CommsVerTest <> 'ABC123' then
raise exception.create('Invalid Comms');

//Get Result Data Back from Server
DATA.Clear;
MyIdTCPClient.IOHandler.ReadStream(DATA, -1, False);
end;

话虽如此,如果您有多个线程同时写入同一个套接字,或者多个线程同时从同一个套接字读取,它们将破坏彼此的数据(或更糟)。您需要同步对套接字的访问,例如至少与关键部分同步。由于您对 TIdTCPClient 的多线程使用,您确实需要重新考虑您的整体客户端设计。

至少,使用您现有的逻辑,当您需要发送命令并读取响应时,停止计时器并等待任何待处理的数据被交换,然后再发送命令,并且不允许其他任何东西访问套接字直到回复回来。您试图一次做太多事情而不同步所有内容以避免重叠。

从长远来看,从单个专用线程进行所有读取,然后根据需要将任何接收到的数据传递给其他线程进行处理会更安全。但这也意味着更改您的发送逻辑以匹配。您可以:
  • 如果您的协议(protocol)允许您并行运行多个命令,那么您可以随时从任何线程发送命令(只需确保使用关键部分以避免重叠),但不要立即等待响应。让每个发送线程继续前进并做其他事情,并让读取线程在预期响应实际到达时异步通知适当的发送线程。
  • 如果协议(protocol)不允许并行命令,但您仍然需要每个发送线程等待其各自的响应,则给套接字线程一个线程安全队列,其他线程可以在需要时将命令推送到其中。然后,套接字线程可以在该队列中运行,定期发送每个命令并根据需要一次接收一个响应。每个将命令放入队列的线程都可以包含一个 TEvent在响应到达时发出信号,这样它们在等待时进入有效的 sleep 状态,但您保留每个线程的等待逻辑。
  • 关于multithreading - 带有计时器事件和其他多线程客户端事件的 Delphi tIdTCPClient,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18313909/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com