Jer*_*dge 2 delphi string multithreading buffer indy
我正在通过Indy套接字处理流式数据包字符串,在客户端,我有一个线程,它从中读取传入的数据,TIdTCPClient并不断将此数据附加到单个字符串缓冲区的末尾.我有另一个线程,它从头开始连续读取此缓冲区,根据需要复制(和删除)数据(一次一个完整的数据包).
我知道在任何情况下访问同一个变量的两个线程都是危险的.但这也适用于字符串吗?或只是对象?从两个不同的线程读取/写入相同的字符串,我能感觉安全吗?如果没有,那么我该怎么做才能保护这个字符串呢?这是一个名为的普通字符串FBuffer.
我正在将数据添加到最后:
procedure TListenThread.CheckForData;
begin
if FClientSocket.Connected then begin
FClientSocket.IOHandler.CheckForDataOnSource(5000);
if not FClientSocket.IOHandler.InputBufferIsEmpty then
FBuffer:= FBuffer + FClientSocket.IOHandler.InputBufferAsString;
end;
end;
Run Code Online (Sandbox Code Playgroud)
另一个线程是这样读取它:
procedeure TPacketThread.CheckForPacket;
var
P: Integer; //Deliminator position
T: String; //Temp copying string
Z: Integer; //Expected packet size
begin
P:= Pos('#', FBuffer);
if P > 0 then begin //Is the deliminator found?
T:= Copy(FBuffer, 1, P-1); //Copy up to deliminator...
Z:= StrToIntDef(T, 0); //Convert packet size to integer...
if Z > 0 then begin
//Is there a full packet waiting in buffer?
if Length(FBuffer) >= Z then begin
//First, delete size definition and deliminator...
Delete(FBuffer, 1, P);
//Now grab the rest of it up to the packet size...
T:= Copy(FBuffer, 1, Z);
//Delete what we just copied...
Delete(FBuffer, 1, Z);
//Finally, pass this packet string for further processing...
ProcessPacket(T);
end;
end;
end;
end;
Run Code Online (Sandbox Code Playgroud)
代码是我的代码的简化版本,只是为了演示我需要做的所有工作FBuffer.
是的,当您从多个线程访问时必须保护字符串,您可以使用crtical部分来执行此操作.看一下EnterCriticalSection,LeaveCriticalSection,InitializeCriticalSection和DeleteCriticalSection函数.
是的,您必须保护字符串缓冲区不受并发访问的影响.Indy有一个TIdThreadSafeString可以用于此目的的课程,例如:
FBuffer: TIdThreadSafeString;
// make sure to Create() and Free() as needed..
Run Code Online (Sandbox Code Playgroud)
.
procedure TListenThread.CheckForData;
begin
if FClientSocket.Connected then begin
FClientSocket.IOHandler.CheckForDataOnSource(5000);
if not FClientSocket.IOHandler.InputBufferIsEmpty then
FBuffer.Append(FClientSocket.IOHandler.InputBufferAsString);
end;
end;
Run Code Online (Sandbox Code Playgroud)
.
procedure TPacketThread.CheckForPacket;
var
P: Integer; //Deliminator position
T: String; //Temp copying string
Z: Integer; //Expected packet size
begin
FBuffer.Lock;
try
P:= Pos('#', FBuffer.Value);
if P > 0 then begin //Is the deliminator found?
T := Copy(FBuffer.Value, 1, P-1); //Copy up to deliminator...
Z := StrToIntDef(T, 0); //Convert packet size to integer...
if Z > 0 then begin
//Is there a full packet waiting in buffer?
if Length(FBuffer.Value) >= Z then begin
//First, delete size definition and deliminator...
FBuffer.Value := Copy(FBuffer.Value, P+1, MaxInt);
//Now grab the rest of it up to the packet size...
T := Copy(FBuffer.Value, 1, Z);
//Delete what we just copied...
FBuffer.Value := Copy(FBuffer.Value, Z+1, MaxInt);
//Finally, pass this packet string for further processing...
ProcessPacket(T);
end;
end;
end;
finally
FBuffer.Unlock;
end;
end;
Run Code Online (Sandbox Code Playgroud)
话虽如此,鉴于你所展示的数据包格式化,我会采取不同的策略:
FBuffer: TIdThreadSafeStringList;
// make sure to Create() and Free() as needed..
Run Code Online (Sandbox Code Playgroud)
.
procedure TListenThread.CheckForData;
var
T: String; //Temp copying string
Z: Integer; //Expected packet size
begin
if FClientSocket.Connected then begin
if FClientSocket.IOHandler.InputBufferIsEmpty then begin
FClientSocket.IOHandler.CheckForDataOnSource(5000);
if FClientSocket.IOHandler.InputBufferIsEmpty then Exit;
end;
// data is available, keep reading as long as packets are present...
repeat
T := FClientSocket.IOHandler.ReadLn('#');
Z := StrToIntDef(T, 0);
if Z > 0 then begin
T := FClientSocket.IOHandler.ReadString(Z);
FBuffer.Add(T);
end;
until FClientSocket.IOHandler.InputBufferIsEmpty;
end;
end;
Run Code Online (Sandbox Code Playgroud)
.
procedure TPacketThread.CheckForPacket;
var
L: TStringList;
T: String;
begin
L := FBuffer.Lock;
try
if L.Count = 0 then Exit;
T := L[0];
L.Delete(0);
finally
FBuffer.Unlock;
end;
ProcessPacket(T);
end;
Run Code Online (Sandbox Code Playgroud)