调用TThread.Synchronize时,System.TMonitor.GetFieldAddress中的间歇性访问冲突,使用Delphi XE5编译
精确时间源(硬件)应该每秒更新一次GUI及其状态。非VCL线程管理它,然后调用Synchronize来更新GUI。在10000-100000次每秒中断后,VCL线程有一个访问冲突,实际上没有调用该时间同步的函数,并且TThread.Synchronize永远不会返回。
非VCL-Thread堆栈和代码段:
:77a47074 ntdll.KiFastSystemCallRet
:77a46a14 ntdll.ZwWaitForSingleObject + 0xc
:751fc3d3 kernel32.WaitForSingleObjectEx + 0x43
:751fc382 kernel32.WaitForSingleObject + 0x12
System.SysUtils.WaitForSyncWaitObj(???,???)
System.SysUtils.WaitOrSignalObj(???,$FFFFFFFF,???)
System.TMonitor.Wait($4843710,4294967295)
System.TMonitor.Wait($4853490,$2E30D40,4294967295)
System.Classes.TThread.Synchronize($898B0A0,False)
System.Classes.TThread.Synchronize((TimeChannel.TTimeChannel.UpdateReaderStatus,$A81CB40))
...
if ( not Terminated ) and ( not Paused ) and assigned( self ) and assigned( fParent ) then
begin
Inc( fParent.fUpdateReaderStatusSyncronizeCounter ); // Counter to see if fParent.UpdateReaderStatus is actually called when Synchronize is called.
Synchronize( fParent.UpdateReaderStatus ); // Called about once per second. Runs for around a day before problem. Thread never returns from here after problem happens.
...
TimeChannel.tRdrStatusControl.Execute
System.Classes.ThreadProc($898B080)
System.ThreadWrapper($8975DA0)
:751fee1c kernel32.BaseThreadInitThunk + 0x12
:77a6399b ntdll.RtlInitializeExceptionChain + 0xef
:77a6396e ntdll.RtlInitializeExceptionChain + 0xc2
VCL Thread stack和code snippets:
...
PROCEDURE tTimeChannel.UpdateReaderStatus;
BEGIN
try
Inc( fUpdateReaderStatusCounter ); // counter remains 1 behind the other counter when problem occurs, so UpdateReaderStatus never got called that time and the other thread never returned from Synchronize.
...
class function TMonitor.GetFieldAddress(const AObject: TObject): PPMonitor;
begin
Result := PPMonitor(PByte(AObject) + AObject.InstanceSize - hfFieldSize + hfMonitorOffset); // Access Violation, read of address 0
end;
...
System.TMonitor.GetFieldAddress(nil)
System.TMonitor.GetMonitor(???)
System.TMonitor.Pulse(nil)
...
function CheckSynchronize(Timeout: Integer = 0): Boolean;
var
SyncProc: PSyncProc;
LocalSyncList: TList;
begin
{$IF Defined(MSWINDOWS)}
if SyncEvent = 0 then
Exit(False);
{$ELSEIF Defined(POSIX)}
if (SyncEvent.ReadDes = 0) or (SyncEvent.WriteDes = 0) then
Exit(False);
{$ENDIF POSIX}
if TThread.CurrentThread.ThreadID <> MainThreadID then
raise EThread.CreateResFmt(@SCheckSynchronizeError, [TThread.CurrentThread.ThreadID]);
if Timeout > 0 then
WaitForSyncEvent(Timeout)
else
ResetSyncEvent;
LocalSyncList := nil;
TMonitor.Enter(ThreadLock);
try
Pointer(LocalSyncList) := AtomicExchange(Pointer(SyncList), Pointer(LocalSyncList));
try
Result := (LocalSyncList <> nil) and (LocalSyncList.Count > 0);
if Result then
begin
while LocalSyncList.Count > 0 do
begin
SyncProc := LocalSyncList[0];
LocalSyncList.Delete(0);
TMonitor.Exit(ThreadLock);
try
try
if Assigned(SyncProc.SyncRec.FMethod) then
SyncProc.SyncRec.FMethod()
else if Assigned(SyncProc.SyncRec.FProcedure) then
SyncProc.SyncRec.FProcedure();
except
if not SyncProc.Queued then
SyncProc.SyncRec.FSynchronizeException := AcquireExceptionObject
else
raise;
end;
finally
TMonitor.Enter(ThreadLock);
end;
if not SyncProc.Queued then
TMonitor.Pulse(SyncProc.Signal) // SyncProc.Signal is nil <----------------
else
begin
Dispose(SyncProc.SyncRec);
Dispose(SyncProc);
end;
end;
end;
finally
LocalSyncList.Free;
end;
finally
TMonitor.Exit(ThreadLock);
end;
end;
...
System.Classes.CheckSynchronize(???)
Vcl.Forms.TApplication.WndProc((0, 0, 0, 0, 0, 0, (), 0, 0, (), 0, 0, ()))
System.Classes.StdWndProc(590087812,0,0,0)
:752ac4e7 ; C:\Windows\system32\USER32.dll
:752ac5e7 ; C:\Windows\system32\USER32.dll
:752acc19 ; C:\Windows\system32\USER32.dll
:752acc70 USER32.DispatchMessageW + 0xf
Vcl.Forms.TApplication.ProcessMessage(???)
:006202fc TApplication.ProcessMessage + $F8
它有99.99%的时间是有效的,所以出了什么问题,我怎样才能让它继续工作,最后0.01%的时间最终冻结线程?
谢谢, 迪伦