Delphi 高速运行时字符串处理出错

wuyihua358 2014-03-18 01:07:39

unit MyMemo;

interface

uses
System.SysUtils,
Windows, System.Classes;

type
TMyMemo = class
private const
FDebug: Boolean = true;
private
FSize: Integer;
FPMemo: pointer;
FLogfile: TStringList;
procedure FDebugOut(const BugText: String);
public
Used: Integer;
constructor Create(Num: Integer);
destructor Destroy; override;
property Size: Integer read FSize;
property PMemo: pointer read FPMemo;
function Write(const Buf: pointer; Num: Integer): Integer;
function Read(Num: Integer): String;
function Buf_Read(out Save_Buf: Integer): Boolean;
procedure Cute(Num: Integer);
end;

const
Buf_head = #2 + '0302+';
Buf_tail = #3;
Lpos = 14;

implementation

constructor TMyMemo.Create(Num: Integer);
begin
inherited Create;
FPMemo := AllocMem(Num);
FSize := Num - 1;
Used := 0;

if FDebug then
// begin
FLogfile := TStringList.Create;
// TThread.CreateAnonymousThread(
// procedure
// var
// OldCount: Integer;
// begin
// OldCount := FLogfile.Count;
// while True do
// begin
// Sleep(1);
// if OldCount <> FLogfile.Count then
// FLogfile.SaveToFile('C:\debug.txt');
// OldCount := FLogfile.Count;
// end;
// end).Start;
// end;
end;

destructor TMyMemo.Destroy;
begin
if FDebug then
FLogfile.Free;

FreeMem(FPMemo);
inherited Destroy;
end;

procedure TMyMemo.FDebugOut(const BugText: String);
begin
FLogfile.Add(Format('%d: %s', [GetTickCount, BugText]));

end;

function TMyMemo.Write(const Buf: pointer; Num: Integer): Integer;
begin
Result := 1;
if Num > Size - Used then
begin
Result := 0;
exit;
end;

Move(Buf^, ptr(LongWord(PMemo) + Used)^, Num);
Used := Used + Num;

if FDebug then
FDebugOut(Format('Calling Write, Size: %d Used: %d', [Num, Used]));
end;

function TMyMemo.Read(Num: Integer): String;
begin
Result := '';
if Num > Used then
begin
raise Exception.Create('No so much to read!');
exit;
end;

if FDebug then
FDebugOut(Format('Using Read, From: %d To: %d Used: %d',
[LongWord(PMemo) + Num, LongWord(PMemo), Used]));

Result := Copy(Pansichar(PMemo), 0, Num);
Cute(Num);
end;

procedure TMyMemo.Cute(Num: Integer);
begin

if Num > Used then
begin
raise Exception.Create('No so much to cute!');
exit;
end;
if Used + Num > Size then
begin
raise Exception.Create('MyMemo is used up!');
exit;
end;

if FDebug then
FDebugOut(Format('Using Cut, From: %d To: %d Used: %d',
[LongWord(PMemo) + Num, LongWord(PMemo), Used]));

Move(ptr(LongWord(PMemo) + Num)^, PMemo^, Used);
Used := Used - Num;
end;

function TMyMemo.Buf_Read(out Save_Buf: Integer): Boolean;
var
Pos_over: Integer;
Buf: ansiString;
begin
Result := false;

Pos_over := Pos(Buf_tail, Pansichar(PMemo));
while (Pos_over > 0) do
begin
if Pos_over > Lpos then
Cute(Pos_over - Lpos) // 86
else if Pos_over < Lpos then
Cute(Length(Buf_tail) + Pos_over - 1)
else
begin // 86
if FDebug then
FDebugOut(Format('Calling COPY, Begin: %d, Length: %d',
[Pos_over - Lpos, Length(Buf_head)]));

Buf := Copy(Pansichar(PMemo), Pos_over - Lpos, Length(Buf_head));

if FDebug then
FDebugOut(Format('Begin P2, BUF: %s', [Buf]));

if Buf <> Buf_head then
Cute(Length(Buf_tail) + Pos_over - 1)
else
begin
Cute(Length(Buf_head));
Buf := Read(Pos_over - Length(Buf_head) - 1);
Cute(Length(Buf_tail));
try
if FDebug then
FDebugOut(Format('Begin P3, BUF: %s', [Buf]));

Save_Buf := StrToInt(Buf);
Result := true;
break;
except
end;
end;
end;
Pos_over := Pos(Buf_tail, Pansichar(PMemo));
sleep(1);

if FDebug then
FDebugOut(Format('New while, Pos: %d', [Pos_over]));
end;

end;

end.

这个字符串处理类,的Buf_Read方法,为什么在循环高速运行时会出现报错,有时是无效指针,有时是堆栈溢出?
我是用在一个高速运行的设备上做数据截取功能的,但是,就是运行了万把次循环的时候,会出错。
Create方法声请的空间是足够大的。

求大神指点。
...全文
217 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
蓝色光芒 2014-03-21
  • 打赏
  • 举报
回复
动态数组,主线程Timer读和其他线程写,是不行的,当其他线程正在执行SetLength时,主线程刚好取完指针,读指针所在位置的长度时,可能面临无效,这样的操作最好是加锁
wuyihua358 2014-03-21
  • 打赏
  • 举报
回复
非常感谢kiboisme,确实是这个问题
KEFU 2014-03-20
  • 打赏
  • 举报
回复
确实有这样的问题,原来曾经写一个繁忙的tcp服务端,有时候出现你这样类似的情况,delphi的自带内存分配有些问题,可以采用FastMM或者自己自己写缓冲区避免这个问题。
wuyihua358 2014-03-20
  • 打赏
  • 举报
回复
感谢5L的帮助。 这个程序,主进程,调用了一个线程来对数据进行处理。其中,有个动态数组,还有它的长度,以及这个类的实例的对象,是作为全局变量存在的。 而主进程,在调用了这个线程之后,只执行一个timer,不断刷新显示 动态数组的长度 和 类里的used的值。 我就发现,当把timer关掉的时候,不会出现问题,如果开启timer,不定时的会出现堆栈溢出或者寻址错误。可是感觉这个timer只是显示出一些全局变量的数值,并没有对他们进行修改。不知道问题是不是出在这里。难道这样也要给变量加锁吗? 请大神指点
蓝色光芒 2014-03-19
  • 打赏
  • 举报
回复
就代码来说,发现问题,把一块内存转换成字符串时,用PAnsiChar(PMemo));是不严谨的,因为这个转换当遇到#0时(即0x00)时,就会认为字符串结束了,忽略后面的内容了。 正确的作法:

function TMyMemo.Read(Num: Integer): AnsiString; //最好定义为AnsiString
begin
  Result := '';
  if .....
  //Result := Copy(Pansichar(PMemo), 0, Num);改成以下代码
 SetLength(Result , Num);
  Move(PMemo^ , Pointer(Result)^ , Num);
  Cute(Num);
end;
后面的Buf_Read函数也需要修改,可以写一个内存位置搜索的函数来代替Pos
wuyihua358 2014-03-19
  • 打赏
  • 举报
回复
虽然是多线程,但是主进程,只发出一条线程之后,主进程就什么都不作了,因此,测试中没有多线程加锁 测试地方,是保存了设备发送了几分钟的几兆的数据,然后发回去,只是简单的一段间隔发数据的代码,就两三行,感觉也不会出问题。 因此,觉得问题可能出在这个类的 move 或者 lop上。不知道,这两个方法,是不是有其他要注意的地方我没注意到。
武稀松 2014-03-18
  • 打赏
  • 举报
回复
Delphi本身久经考验,楼主确定一下自己的代码没问题? 多线程没加锁?
stevenpeng 2014-03-18
  • 打赏
  • 举报
回复
确定代码没问题吗
lght 2014-03-18
  • 打赏
  • 举报
回复
测试代码呢? 另外字符串字节长度是Length(str) * SizeOf(Char)

1,183

社区成员

发帖
与我相关
我的任务
社区描述
Delphi Windows SDK/API
社区管理员
  • Windows SDK/API社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧