InitializeCriticalSection 在一个项目中有效,但在另一个项目中失败
InitializeCriticalSection works in one project, but fails in another
在 Windows 10 x64 上使用 Visual Studio 2019 Professional。我有几个 C++ DLL 项目,其中一些是多线程的。我正在使用 CRITICAL_SECTION
对象来确保线程安全。
在 DLL1 中:
CRITICAL_SECTION critDLL1;
InitializeCriticalSection(&critDLL1);
在 DLL2 中:
CRITICAL_SECTION critDLL2;
InitializeCriticalSection(&critDLL2);
当我将 critDLL1 与 EnterCriticalSection
或 LeaveCriticalSection
一起使用时,在 _DEBUG
或 NDEBUG
模式下一切正常。但是当我使用 critDLL2 时,我在 NDEBUG
中的 'ntdll.dll' 中遇到了访问冲突(尽管在 _DEBUG
中没有)。
在 NDEBUG
模式下弹出消息框后,我最终能够将问题追溯到第一次使用 EnterCriticalSection
。
可能导致 CRITICAL_SECTION
在一个项目中失败但在其他项目中工作的原因是什么? MSDN page 没有帮助。
更新 1
比较 DLL1(工作)和 DLL2(不工作)的项目设置后,我不小心让 DLL2 工作了。我已经通过恢复到早期版本(崩溃)然后更改项目(没有崩溃!)来确认这一点。
这是设置:
Project Properties > C/C++ > Optimization > Whole Program Optimization
将其设置为 Yes (/GL)
,我的程序就会崩溃。将其更改为 No
并且它工作正常。 /GL
开关有什么作用,为什么会导致崩溃?
更新 2
@Acorn 的精彩回答和@RaymondChen 的评论,提供了追踪并解决问题的线索。有两个问题(都是程序员错误)。
问题 1
Whole Program Optimzation
(wPO) 的假设是 MSVC 编译器正在编译 "the whole program"。对于我的 DLL 项目来说,这是一个错误的假设,它在内部使用第 3 方库,然后又被用 Delphi 编写的外部应用程序使用。此设置默认设置为 Yes (/GL)
,但应设置为 No
。这感觉像是 Visual Studio 中的错误,但无论如何,程序员需要意识到这一点。我不知道 WPO
的所有详细信息,但至少对于打算由其他应用程序使用的 DLL,应该更改默认值。
问题 2
严重的程序员错误。这是对第 3 方库的调用,它返回了一个 128 字节的 ASCII 代码,这是错误:
// Before
// m_config::acSerial defined as "char acSerial[21]"
(void) m_pLib->GetPara(XPARA_PRODUCT_INFO, &m_config.acSerial[0]);
EnterCriticalSection(&crit); // Crash!
// After
#define SERIAL_LEN 20
// m_config::acSerial defined as "char acSerial[SERIAL_LEN+1]"
//...
char acSerial[128];
(void) m_pLib->GetPara(XPARA_PRODUCT_INFO, &acSerial[0]);
strncpy(m_config.acSerial, acSerial, max(SERIAL_LEN, strlen(acSerial)));
EnterCriticalSection(&crit); // Works!
错误,现在很明显,是第 3 方库没有将设备的序列号复制到我提供的 char*
中...它复制了 128 个字节到我的 char*
stomping acSerial
之后内存中所有连续的内容。之前没有注意到这一点,因为 m_pLib->GetPara(XPARA_PRODUCT_INFO, ...)
是对第 3 方库的首次调用之一,而其余的连续数据此时大部分为 NULL。
问题与 CRITICAL_SECTION
无关。感谢 Acorn 和 RaymondChen...宇宙的这个角落恢复了理智。
如果您的程序在 WPO(假设您正在编译的是整个程序的优化)下崩溃,则意味着该假设不正确或优化器最终利用了一些以前没有的未定义行为(没有应用优化),即使假设是正确的。
一般来说,除非您真的确定自己满足他们的要求,否则请避免启用优化。
如需进一步分析,请提供MRE。
在 Windows 10 x64 上使用 Visual Studio 2019 Professional。我有几个 C++ DLL 项目,其中一些是多线程的。我正在使用 CRITICAL_SECTION
对象来确保线程安全。
在 DLL1 中:
CRITICAL_SECTION critDLL1;
InitializeCriticalSection(&critDLL1);
在 DLL2 中:
CRITICAL_SECTION critDLL2;
InitializeCriticalSection(&critDLL2);
当我将 critDLL1 与 EnterCriticalSection
或 LeaveCriticalSection
一起使用时,在 _DEBUG
或 NDEBUG
模式下一切正常。但是当我使用 critDLL2 时,我在 NDEBUG
中的 'ntdll.dll' 中遇到了访问冲突(尽管在 _DEBUG
中没有)。
在 NDEBUG
模式下弹出消息框后,我最终能够将问题追溯到第一次使用 EnterCriticalSection
。
可能导致 CRITICAL_SECTION
在一个项目中失败但在其他项目中工作的原因是什么? MSDN page 没有帮助。
更新 1
比较 DLL1(工作)和 DLL2(不工作)的项目设置后,我不小心让 DLL2 工作了。我已经通过恢复到早期版本(崩溃)然后更改项目(没有崩溃!)来确认这一点。
这是设置:
Project Properties > C/C++ > Optimization > Whole Program Optimization
将其设置为 Yes (/GL)
,我的程序就会崩溃。将其更改为 No
并且它工作正常。 /GL
开关有什么作用,为什么会导致崩溃?
更新 2
@Acorn 的精彩回答和@RaymondChen 的评论,提供了追踪并解决问题的线索。有两个问题(都是程序员错误)。
问题 1
Whole Program Optimzation
(wPO) 的假设是 MSVC 编译器正在编译 "the whole program"。对于我的 DLL 项目来说,这是一个错误的假设,它在内部使用第 3 方库,然后又被用 Delphi 编写的外部应用程序使用。此设置默认设置为 Yes (/GL)
,但应设置为 No
。这感觉像是 Visual Studio 中的错误,但无论如何,程序员需要意识到这一点。我不知道 WPO
的所有详细信息,但至少对于打算由其他应用程序使用的 DLL,应该更改默认值。
问题 2
严重的程序员错误。这是对第 3 方库的调用,它返回了一个 128 字节的 ASCII 代码,这是错误:
// Before
// m_config::acSerial defined as "char acSerial[21]"
(void) m_pLib->GetPara(XPARA_PRODUCT_INFO, &m_config.acSerial[0]);
EnterCriticalSection(&crit); // Crash!
// After
#define SERIAL_LEN 20
// m_config::acSerial defined as "char acSerial[SERIAL_LEN+1]"
//...
char acSerial[128];
(void) m_pLib->GetPara(XPARA_PRODUCT_INFO, &acSerial[0]);
strncpy(m_config.acSerial, acSerial, max(SERIAL_LEN, strlen(acSerial)));
EnterCriticalSection(&crit); // Works!
错误,现在很明显,是第 3 方库没有将设备的序列号复制到我提供的 char*
中...它复制了 128 个字节到我的 char*
stomping acSerial
之后内存中所有连续的内容。之前没有注意到这一点,因为 m_pLib->GetPara(XPARA_PRODUCT_INFO, ...)
是对第 3 方库的首次调用之一,而其余的连续数据此时大部分为 NULL。
问题与 CRITICAL_SECTION
无关。感谢 Acorn 和 RaymondChen...宇宙的这个角落恢复了理智。
如果您的程序在 WPO(假设您正在编译的是整个程序的优化)下崩溃,则意味着该假设不正确或优化器最终利用了一些以前没有的未定义行为(没有应用优化),即使假设是正确的。
一般来说,除非您真的确定自己满足他们的要求,否则请避免启用优化。
如需进一步分析,请提供MRE。