印地 gettickdiff64() 18446744073709551600 问题
indy gettickdiff64() 18446744073709551600 problem
我发现 gettickdiff64 函数有时会导致 18446744073709551600(或 18446744073709551601)并导致
编程到 运行 不正确。
通常没有大于 300000
的结果
这可能是关于什么的?
我是否应该始终针对此问题进行额外检查?
32位VCL应用程序。
我使用 Delphi 10.4.1(其独立版本 10.6.2.0)
运行 on: 64 bit Windows Server 2012 R2 Foundation / intel xeon cpu E3-1225 v5 3.3 Ghz.
代码结构如下:
TMyClass = class
private
//.............
lastSetTime: uint64;
critic: TCriticalSection;
public
//.............
procedure setLastSetTime( ltime: uint64 );
function getLastSetTime: uint64;
end;
procedure TMyClass.setLastSetTime( ltime: uint64 );
begin
critic.enter;
try
lastSetTime := ltime;
finally
critic.leave;
end;
end;
function TMyClass.getLastSetTime: uint64;
begin
critic.enter;
try
result := lastSetTime;
finally
critic.leave;
end;
end;
...........
procedure controlAll(); //------>this is called from within thread every 5 minutes
var oki: boolean;
starttime, tdiff, ltime: uint64;
i: integer;
myC, sC: TMyClass;
begin
oki := false;
starttime := ticks64();
while ( oki = false ) and ( gettickdiff64( starttime, ticks64 ) < 40000 ) do
begin
//.........
//.........
sC := nil;
with myClassList.LockList do
try
if count > 0 then //---> has about 50000
begin
i := 0;
while i < count do
begin
myC := TMyClass( items[ i ] );
ltime := myC.getLastSetTime();
tdiff := gettickdiff64( ltime, ticks64() );
if tdiff > 50000 then
begin
logToFile( tdiff.ToString + ' ' + ltime.ToString ); //-----> every 5 minutes 50-60 log lines occur like this: 18446744073709551600 468528329
//..........
//.........
sC := myC;
delete( i );
break;
end;
inc( i );
end;
end;
finally
myClassList.UnlockList;
end;
if sC = nil then oki := true
else
begin
//..........
//..........
end;
end;
end;
设置该值的代码结构如下
classListArray 将所有 类 类型的 TMyClass 按服务器和频道编号分组。
myClassList 将所有 类 类型的 TMyClass 一个接一个地附加,而不分组。
classListArray 用于花费更少 CPU 和处理速度更快。
这两个列表在访问 类 时没有相互保护。
只有在增删改写类.
时才会互相保护
classListArray: array[ 1..250, 1..12 ] of TThreadList;
//.................
procedure ServerExecute(AContext: TIdContext);
var Ath: TMypeer;
severNum, channelNum, clientNum, i, j, num: integer;
pSize: word;
stream: Tmemorystream;
packageNum: byte;
begin
try
Ath := TMypeer( AContext );
serverNum := Ath.getServerNum();
channelNum := Ath.getChannelNum();
Ath.SendQueue();
if AContext.Connection.IOHandler.InputBufferIsEmpty then
if not AContext.Connection.IOHandler.CheckForDataOnSource( 50 ) then Exit;
clientNum := AContext.Connection.IOHandler.ReadInt32( false );
pSize := AContext.Connection.IOHandler.ReadUInt16( false );
stream := TMemorystream.create;
try
AContext.Connection.IOHandler.ReadStream( stream, pSize );
stream.Seek( 0, soFromBeginning );
if clientNum <> 0 then
begin
//...........
end
else
begin
stream.ReadBuffer( packageNum, sizeof( packageNum ) );
if packageNum = 10 then
begin
stream.ReadBuffer( num, sizeof( num ) );
for i := 1 to num do
begin
stream.ReadBuffer( clientNum, sizeof( clientNum ) );
with classListArray[ serverNum, channelNum ].LockList do
try
if count > 0 then
for j := 0 to count - 1 do
begin
if TMyClass( items[ j ] ).getClientNum = clientNum then
begin
TMyClass( items[ j ] ).setLastSetTime( ticks64 ); //**********
break;
end;
end;
finally
classListArray[ serverNum, channelNum ].unLockList;
end;
end;
end
else
//.........
end;
finally
stream.free;
end;
except on e:exception do
begin
if E is Eidexception then raise
else
begin
logToFile( e.message );
//..........
end;
end;
end;
end;
根据您的日志,ltime
是 468528329
和 GetTickDiff64(ltime, Ticks64())
return 编辑 18446744073709551600
。给定 GetTickDiff64()
的简单实现(其中 TIdTicks
是 UInt64
):
function GetTickDiff64(const AOldTickCount, ANewTickCount: TIdTicks): TIdTicks;
{$IFDEF USE_INLINE}inline;{$ENDIF}
begin
{This is just in case the TickCount rolled back to zero}
if ANewTickCount >= AOldTickCount then begin
Result := TIdTicks(ANewTickCount - AOldTickCount);
end else begin
Result := TIdTicks(((High(TIdTicks) - AOldTickCount) + ANewTickCount) + 1);
end;
end;
给定 AOldTickCount=468528329
,此代码可以 return 18446744073709551600
的唯一方法是 ANewTickCount
是 18446744074178079929
或 468528313
。
由于 VCL 仅在 Windows 上运行,并且在 Windows 上运行 Ticks64()
只是 Win32 GetTickCount64()
函数在 Vista 及更高版本上的薄包装,它非常Windows 不太可能为当前的滴答计数器产生像 18446744074178079929
这样的天文数字(即从启动后的 213503982340 天)。所以它必须 returned 468528313
代替,这更合理(从启动后仅 5.4 天)。那比 ltime=468528329
少 16 毫秒,所以 GetTickDiff64()
会假设 Windows' 滴答计数器已经超过 High(UInt64)
并回绕到0(这对于 64 位滴答计数器来说不太可能 在我们的一生中做到。
因此,您需要调试代码并弄清楚 Ticks64()
/Windows 如何可能 return 468528329
然后 return 468528313
。我怀疑它真的没有这样做,而且您的代码中更有可能存在我们看不到的错误,它正在将错误的值存储到 TMyClass.lastSetTime
开始。
也就是说,您可以考虑摆脱 TCriticalSection
的开销,并使用 TInterlocked
代替 read/write 您的 UInt64
成员原子地。
或者,尝试使用 Delphi 自己的 TStopWatch
而不是手动跟踪报价。
我发现 gettickdiff64 函数有时会导致 18446744073709551600(或 18446744073709551601)并导致 编程到 运行 不正确。 通常没有大于 300000
的结果这可能是关于什么的? 我是否应该始终针对此问题进行额外检查?
32位VCL应用程序。 我使用 Delphi 10.4.1(其独立版本 10.6.2.0) 运行 on: 64 bit Windows Server 2012 R2 Foundation / intel xeon cpu E3-1225 v5 3.3 Ghz.
代码结构如下:
TMyClass = class
private
//.............
lastSetTime: uint64;
critic: TCriticalSection;
public
//.............
procedure setLastSetTime( ltime: uint64 );
function getLastSetTime: uint64;
end;
procedure TMyClass.setLastSetTime( ltime: uint64 );
begin
critic.enter;
try
lastSetTime := ltime;
finally
critic.leave;
end;
end;
function TMyClass.getLastSetTime: uint64;
begin
critic.enter;
try
result := lastSetTime;
finally
critic.leave;
end;
end;
...........
procedure controlAll(); //------>this is called from within thread every 5 minutes
var oki: boolean;
starttime, tdiff, ltime: uint64;
i: integer;
myC, sC: TMyClass;
begin
oki := false;
starttime := ticks64();
while ( oki = false ) and ( gettickdiff64( starttime, ticks64 ) < 40000 ) do
begin
//.........
//.........
sC := nil;
with myClassList.LockList do
try
if count > 0 then //---> has about 50000
begin
i := 0;
while i < count do
begin
myC := TMyClass( items[ i ] );
ltime := myC.getLastSetTime();
tdiff := gettickdiff64( ltime, ticks64() );
if tdiff > 50000 then
begin
logToFile( tdiff.ToString + ' ' + ltime.ToString ); //-----> every 5 minutes 50-60 log lines occur like this: 18446744073709551600 468528329
//..........
//.........
sC := myC;
delete( i );
break;
end;
inc( i );
end;
end;
finally
myClassList.UnlockList;
end;
if sC = nil then oki := true
else
begin
//..........
//..........
end;
end;
end;
设置该值的代码结构如下
classListArray 将所有 类 类型的 TMyClass 按服务器和频道编号分组。 myClassList 将所有 类 类型的 TMyClass 一个接一个地附加,而不分组。 classListArray 用于花费更少 CPU 和处理速度更快。 这两个列表在访问 类 时没有相互保护。 只有在增删改写类.
时才会互相保护classListArray: array[ 1..250, 1..12 ] of TThreadList;
//.................
procedure ServerExecute(AContext: TIdContext);
var Ath: TMypeer;
severNum, channelNum, clientNum, i, j, num: integer;
pSize: word;
stream: Tmemorystream;
packageNum: byte;
begin
try
Ath := TMypeer( AContext );
serverNum := Ath.getServerNum();
channelNum := Ath.getChannelNum();
Ath.SendQueue();
if AContext.Connection.IOHandler.InputBufferIsEmpty then
if not AContext.Connection.IOHandler.CheckForDataOnSource( 50 ) then Exit;
clientNum := AContext.Connection.IOHandler.ReadInt32( false );
pSize := AContext.Connection.IOHandler.ReadUInt16( false );
stream := TMemorystream.create;
try
AContext.Connection.IOHandler.ReadStream( stream, pSize );
stream.Seek( 0, soFromBeginning );
if clientNum <> 0 then
begin
//...........
end
else
begin
stream.ReadBuffer( packageNum, sizeof( packageNum ) );
if packageNum = 10 then
begin
stream.ReadBuffer( num, sizeof( num ) );
for i := 1 to num do
begin
stream.ReadBuffer( clientNum, sizeof( clientNum ) );
with classListArray[ serverNum, channelNum ].LockList do
try
if count > 0 then
for j := 0 to count - 1 do
begin
if TMyClass( items[ j ] ).getClientNum = clientNum then
begin
TMyClass( items[ j ] ).setLastSetTime( ticks64 ); //**********
break;
end;
end;
finally
classListArray[ serverNum, channelNum ].unLockList;
end;
end;
end
else
//.........
end;
finally
stream.free;
end;
except on e:exception do
begin
if E is Eidexception then raise
else
begin
logToFile( e.message );
//..........
end;
end;
end;
end;
根据您的日志,ltime
是 468528329
和 GetTickDiff64(ltime, Ticks64())
return 编辑 18446744073709551600
。给定 GetTickDiff64()
的简单实现(其中 TIdTicks
是 UInt64
):
function GetTickDiff64(const AOldTickCount, ANewTickCount: TIdTicks): TIdTicks;
{$IFDEF USE_INLINE}inline;{$ENDIF}
begin
{This is just in case the TickCount rolled back to zero}
if ANewTickCount >= AOldTickCount then begin
Result := TIdTicks(ANewTickCount - AOldTickCount);
end else begin
Result := TIdTicks(((High(TIdTicks) - AOldTickCount) + ANewTickCount) + 1);
end;
end;
给定 AOldTickCount=468528329
,此代码可以 return 18446744073709551600
的唯一方法是 ANewTickCount
是 18446744074178079929
或 468528313
。
由于 VCL 仅在 Windows 上运行,并且在 Windows 上运行 Ticks64()
只是 Win32 GetTickCount64()
函数在 Vista 及更高版本上的薄包装,它非常Windows 不太可能为当前的滴答计数器产生像 18446744074178079929
这样的天文数字(即从启动后的 213503982340 天)。所以它必须 returned 468528313
代替,这更合理(从启动后仅 5.4 天)。那比 ltime=468528329
少 16 毫秒,所以 GetTickDiff64()
会假设 Windows' 滴答计数器已经超过 High(UInt64)
并回绕到0(这对于 64 位滴答计数器来说不太可能 在我们的一生中做到。
因此,您需要调试代码并弄清楚 Ticks64()
/Windows 如何可能 return 468528329
然后 return 468528313
。我怀疑它真的没有这样做,而且您的代码中更有可能存在我们看不到的错误,它正在将错误的值存储到 TMyClass.lastSetTime
开始。
也就是说,您可以考虑摆脱 TCriticalSection
的开销,并使用 TInterlocked
代替 read/write 您的 UInt64
成员原子地。
或者,尝试使用 Delphi 自己的 TStopWatch
而不是手动跟踪报价。