调用TThread.Synchronize时System.TMonitor.GetFieldAddress中存在访问冲突,已使用Delphi XE5编译
调用TThread.Synchronize时System.TMonitor.GetFieldAddress中存在间歇性访问冲突,使用Delphi XE5编译 精确时间源(硬件)应该每秒更新GUI一次其状态。非VCL线程管理它,然后调用Synchronize更新GUI。每秒10000-100000次中断后,VCL线程出现访问冲突,此时实际上没有调用要同步的函数,TThread.Synchronize永远不会返回 非VCL线程堆栈和代码段:调用TThread.Synchronize时System.TMonitor.GetFieldAddress中存在访问冲突,已使用Delphi XE5编译,delphi,delphi-xe5,Delphi,Delphi Xe5,调用TThread.Synchronize时System.TMonitor.GetFieldAddress中存在间歇性访问冲突,使用Delphi XE5编译 精确时间源(硬件)应该每秒更新GUI一次其状态。非VCL线程管理它,然后调用Synchronize更新GUI。每秒10000-100000次中断后,VCL线程出现访问冲突,此时实际上没有调用要同步的函数,TThread.Synchronize永远不会返回 非VCL线程堆栈和代码段: :77a47074 ntdll.KiFastSystemC
: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
...
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
VCL线程堆栈和代码段:
: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
...
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
。。。
程序tTimeChannel.UpdateReaderStatus;
开始
尝试
公司(fUpdateReaderStatusCounter);//当问题发生时,计数器在另一个计数器后面保持1,因此当时从未调用UpdateReaderStatus,而另一个线程也从未从Synchronize返回。
...
类函数TMonitor.GetFieldAddress(const AOObject:ToObject):PPMonitor;
开始
结果:=PPMonitor(PByte(AObject)+AObject.InstanceSize-hfFieldSize+hfMonitorOffset);//访问冲突,读取地址0
结束;
...
System.TMonitor.GetFieldAddress(无)
System.TMonitor.GetMonitor(?)
系统TMonitor.Pulse(无)
...
函数CheckSynchronize(超时:整数=0):布尔值;
变量
SyncProc:PSyncProc;
LocalSyncList:TList;
开始
{$IF-Defined(MSWINDOWS)}
如果SyncEvent=0,则
退出(假);
{$ELSEIF已定义(POSIX)}
如果(SyncEvent.ReadDes=0)或(SyncEvent.WriteDes=0),则
退出(假);
{$ENDIF POSIX}
如果TThread.CurrentThread.ThreadID MainThreadID,则
提高EThread.CreateResFmt(@SCheckSynchronizeError[TThread.CurrentThread.ThreadID]);
如果超时>0,则
WaitForSyncEvent(超时)
其他的
重置同步事件;
LocalSyncList:=nil;
t监控输入(螺纹锁紧);
尝试
指针(LocalSyncList):=AtomicExchange(指针(SyncList),指针(LocalSyncList));
尝试
结果:=(LocalSyncList nil)和(LocalSyncList.Count>0);
如果结果是这样的话
开始
当LocalSyncList.Count>0时
开始
SyncProc:=LocalSyncList[0];
LocalSyncList.Delete(0);
t监控出口(螺纹锁紧);
尝试
尝试
如果已分配(SyncProc.SyncRec.FMethod),则
SyncProc.SyncRec.FMethod()
否则,如果已分配(SyncProc.SyncRec.FProcedure),则
SyncProc.SyncRec.FProcedure();
除了
如果没有SyncProc.排队,则
SyncProc.SyncRec.FSynchronizeException:=AcquireExceptionObject
其他的
提高;
结束;
最后
t监控输入(螺纹锁紧);
结束;
如果没有SyncProc.排队,则
TMonitor.Pulse(SyncProc.Signal)//SyncProc.Signal为零。其他问题的一些答案表示要避免使用Synchronize()。它是从根本上被破坏了,还是很容易出错?其他一些线程也调用synchronize和queue。其他线程是否可能破坏了synchronize中的某些内容,并且错误最终出现在该线程中?SyncProc.Signal
对于TThread.synchronize()
请求,决不能为零Synchronize()
创建一个TObject
并将其分配给SyncProc.Signal
,并将SyncProc.Queued
设置为False,然后再将SyncProc项目添加到队列中。因此,当SyncProc.Queued
为False时,SyncProc.Signal
可能为零的唯一方法是内存被损坏,而队列项是该损坏的不幸受害者。这不是Synchronize()
本身的错误,因此肯定有其他东西会损坏内存。TMonitor
仍然存在缺陷这一点很有可能。您能否将代码减少到最低限度,在短时间内强制出错,即通过更频繁的更新?