我很困惑,我认为我的调试器在骗我。我的代码中有以下循环:
MyClass::UploadFile(CString strFile)
{
...
static DWORD dwLockWaitTime = EngKey::GetDWORD(DNENG_SERVER_UPLOAD_LOCK_WAIT_TIME, DNENG_SERVER_UPLOAD_LOCK_WAIT_TIME_DEFAULT);
static DWORD dwLockPollInterval = EngKey::GetDWORD(DNENG_SERVER_UPLOAD_LOCK_POLL_INTERVAL, DNENG_SERVER_UPLOAD_LOCK_POLL_INTERVAL_DEFAULT);
LONGLONG llReturnedOffset(0LL);
BOOL bLocked(FALSE);
for (DWORD sanity = 0; (sanity == 0 || status == RESUMABLE_FILE_LOCKED) && sanity < (dwLockWaitTime / dwLockPollInterval); sanity++)
{
...
在我的程序过程中,这个循环已经执行了数百次,两个静态变量在代码中的任何地方都没有改变,当它们被静态初始化并在循环条件中读取时,它们只被写入一次在另一个地方。由于它们是从 Windows 注册表中读取的用户设置,因此它们几乎总是具有 dwLockWaitTime = 60 和 dwLockPollInterval = 5 的常量值。所以循环总是执行 60/5。
很少,我得到一个崩溃转储,它表明这行代码已经引发了除以零错误。我检查了 WinDbg 的内容并显示:
FAULTING_IP:
procname!CServerAgent::ResumableUpload+54a [serveragent.cpp @ 725]
00000001`3f72d74a f73570151c00 div eax,dword ptr [proc!dwLockPollInterval (00000001`3f8eecc0)]
EXCEPTION_RECORD: ffffffffffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 000000013f72d74a (proc!CServerAgent::ResumableUpload+0x000000000000054a)
ExceptionCode: c0000094 (Integer divide-by-zero)
ExceptionFlags: 00000000
NumberParameters: 0
ERROR_CODE: (NTSTATUS) 0xc0000094 - {EXCEPTION} Integer division by zero.
我检查了汇编代码,它显示崩溃发生在这个 div 指令上。
00000001`3f72d744 8b0572151c00 mov eax,dword ptr [dwLockWaitTime (00000001`3f8eecbc)]
00000001`3f72d74a f73570151c00 div eax,dword ptr [dwLockPollInterval (00000001`3f8eecc0)]
所以你可以看到 000000013f8eecbc
的值被移动到 eax
然后 eax
除以 000000013f8eecc0 的值
.
你问的这两个值是什么?
0:048> dd 00000001`3f8eecbc
00000001`3f8eecbc 0000003c 00000005 00000001 00000000
00000001`3f8eeccc 00000000 00000002 00000000 00000000
00000001`3f8eecdc 00000000 7fffffff a9ad25cf 7fffffff
00000001`3f8eecec a9ad25cf 00000000 00000000 00000000
00000001`3f8eecfc 00000000 00000000 00000000 00000000
00000001`3f8eed0c 00000000 00000000 00000000 00000000
00000001`3f8eed1c 00000000 00000000 00000000 00000000
00000001`3f8eed2c 00000000 00000000 00000000 00000000
0:048> dd 000000013f8eecc0
00000001`3f8eecc0 00000005 00000001 00000000 00000000
00000001`3f8eecd0 00000002 00000000 00000000 00000000
00000001`3f8eece0 7fffffff a9ad25cf 7fffffff a9ad25cf
00000001`3f8eecf0 00000000 00000000 00000000 00000000
00000001`3f8eed00 00000000 00000000 00000000 00000000
00000001`3f8eed10 00000000 00000000 00000000 00000000
00000001`3f8eed20 00000000 00000000 00000000 00000000
00000001`3f8eed30 00000000 00000000 00000000 00000000
常量 60
和 5
完全符合我的预期。那么除以零在哪里???我的调试器在撒谎吗?肯定是硬件已经抛出了除以零,所以它不会犯错吗?如果它在我的代码中的不同位置被零除,那么调试器会在这个位置显示指令指针的几率是多少?我承认,我被难住了..
最佳答案
由于代码是成员函数的一部分,并且您从多个线程调用此函数,如果使用不符合 C++ 11 的编译器,则 static
变量不是线程安全的标准。因此,在初始化这两个静态变量时,您可能会遇到数据竞争。
对于符合 C++ 11 标准的编译器,静态变量现在保证由第一个线程初始化,而后续线程等待静态变量初始化。
对于 Visual Studio 2010
及更低版本,不保证静态局部变量是线程安全的,因为这些编译器符合 C++ 03 和 C++ 98 标准。
对于 Visual Studio 2013
,我不确定 C++ 11 在静态本地初始化方面的支持水平。因此,对于 Visual Studio 2013,您可能必须使用适当的同步来确保正确初始化静态局部变量。
对于 Visual Studio 2015
,该项目已得到解决,并且正确的静态本地初始化已完全实现,因此您当前拥有的代码应该可以在 VS 2015 及更高版本中正常工作。
编辑:对于 Visual Studio 2013
,未实现静态本地线程安全初始化(“Magic Statics”),as described here .
因此,我们可以谨慎地验证原始问题的原因是静态局部初始化问题和线程。所以解决方案(如果你想坚持使用 VS 2013)是使用适当的同步,或者重新设计你的应用程序,以便不再需要静态变量。
关于c++ - 什么时候除以零而不是除以零?调试器中的一个谜题(静态变量问题),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28096970/