使用std::mutex而不是boost::mutex时出现未处理的异常

11

我试图在代码中摆脱一些 boost 依赖,而是使用新的 C++11 特性(Visual Studio 2013)。

在我的一个组件中,我使用 boost::mutexboost::lock_guard<boost::mutex>,一切正常。但是当我改用 std::mutexstd::lock_guard<std::mutex> 时,当从 main() 返回时发生以下错误:

Unhandled exception at 0x7721E3BE (ntdll.dll) in GrabberTester.exe: 0xC0000005: Access violation reading location 0xA6A6B491.

实际项目非常复杂,因此很难提供完整的工作代码示例以重现此问题。 在我的实际项目中,互斥量在运行时加载的共享库中使用(但应该在我从 main() 返回时卸载)。

我的问题是:

  • boost::mutexstd::mutex 是否被设计为绝对相同的行为?
  • 如果不是,则有什么区别? 当使用 std::mutex 而不是 boost::mutex 时,需要注意什么?
  • 在共享库中,我正在使用 boost::thread 框架创建线程。 std::mutex 只能与 std::thread 一起使用,并且与 boost::thread 不兼容吗?

编辑:

我还注意到一件事:当我卸载动态加载的共享库时,这需要一些时间。(DLL 访问硬件,需要一些时间才能干净地关闭所有内容)。 但是当我切换到 std::mutex 时,似乎 DLL 几乎可以立即卸载,但程序在从 main() 返回时崩溃。 我有这样的印象,即 std::mutex 的问题特别出现在 DLL 的上下文中。

编辑 2:

应用程序和 DLL 都使用 v120 工具集以 Debug 配置进行全新构建,并静态链接了运行时库(/MTd)。

编辑 3:

下面是调用堆栈。异常似乎来自驱动程序的某个地方。只是偶然发现它与我使用的互斥量实现相关。

ntdll.dll!7721e3be()
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
ntdll.dll!7721e023()
kernel32.dll!76b014ad()
msvcr100.dll!71b0016a()
PS1080.dll!oniDriverDestroy() Line 29
OpenNI2.dll!oni::implementation::DeviceDriver::~DeviceDriver() Line 95
OpenNI2.dll!oni::implementation::Context::shutdown() Line 324
OpenNi2Grabber.dll!openni::OpenNI::shutdown() Line 2108
OpenNi2Grabber.dll!GrabberSingletonImpl::~GrabberSingletonImpl() Line 46
OpenNi2Grabber.dll!`GrabberSingletonImpl::getInstance'::`2'::`dynamic atexit destructor for 'inst''()
OpenNi2Grabber.dll!doexit(int code, int quick, int retcaller) Line 628
OpenNi2Grabber.dll!_cexit() Line 448
OpenNi2Grabber.dll!_CRT_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 169
OpenNi2Grabber.dll!__DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 399
OpenNi2Grabber.dll!_DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 340
ntdll.dll!7722b990()
ntdll.dll!77249bad()
ntdll.dll!77249a4f()
kernel32.dll!76b079ed()
GrabberTester.exe!__crtExitProcess(int status) Line 776
GrabberTester.exe!doexit(int code, int quick, int retcaller) Line 678
GrabberTester.exe!exit(int code) Line 417
GrabberTester.exe!__tmainCRTStartup() Line 264
GrabberTester.exe!mainCRTStartup() Line 165
kernel32.dll!76b0338a()
ntdll.dll!7722bf32()
ntdll.dll!7722bf05()

编辑 4:

可能这是OpenNI2 SDK的一个bug,只有在这些非常特定的情况下才能观察到。所以我将openni标签添加到了这个问题中。但问题仍然存在:为什么它可以使用boost::mutex而不是std::mutex


1
据我所知,STL和Boost实现之间没有显著的区别。我见过mutex出现这种情况的唯一原因是一个线程锁定了,而另一个线程试图解锁。我会确保在所有可能的情况下都使用lock_guards,并调查所有其他使用情况,以获取大量同步调试输出。 - ChrisWard1000
2
我知道一些VS2013实现的标准线程设施在各个方面都存在漏洞。不确定mutex是否包含在其中。 - T.C.
2
你有没有可能在混合不同的运行时版本? - Mario
1
还是没有进行完整的重建吗?(即,某些目标文件、DLL文件以及其他文件仍在使用旧代码) - Mat
1
请至少发布崩溃的堆栈跟踪。 - wilx
显示剩余2条评论
4个回答

5
最可能的问题是静态初始化地狱,我最近经历了几乎相同的情况。这里是正在发生的事情:
1. 你有一个静态互斥锁(可以是静态类的成员)。 2. doexit()代码开始清理你的静态内容。 3. 互斥锁在doexit()中的某个位置被销毁。 4. 某些东西在互斥锁被销毁后使用它,通常是在析构函数中。
问题在于你不知道静态对象的销毁顺序。所以如果你有:
static std::mutex staticMutex;

void someFunction()
{
    std::unique_lock<std::mutex> lock(staticMutex);
    doSomethingAwesome();
}

....

StaticObjA::~StaticObjA()
{
    someFunction();
}

当调用~StaticObjA()时,您的静态互斥量可能已经被删除/销毁/填充了无效值(deadbeef)。当对象在不同的编译单元(即在不同的文件中定义)中定义时,问题会更加严重。

我的解决建议是尽量减少对静态对象的依赖,您可以尝试使用一个静态对象来处理所有其他对象的构建/销毁,以便您可以控制事件的顺序。或者干脆不使用静态对象。


问题在于当对象在不同的编译单元中定义时,问题会变得更加严重。在单个翻译单元内,初始化顺序是明确定义的。此外,std::mutex 的构造函数是constexpr的,因此全局的std::mutex应该在任何需要动态初始化的全局对象之前进行初始化,因此应该在这些对象之后被销毁。我认为Microsoft的实现没有使用constexpr,因此不符合该要求。 - Jonathan Wakely

3

当我的代码试图两次锁定同一个互斥锁时,我遇到了类似的问题:一个函数获取了锁,然后调用另一个尝试在相同的全局/静态互斥锁上获取锁的函数。

mutex queueMutex;

void f1()
{
    lock_guard<mutex> guard(queueMutex);
    f2();
}

void f2()
{
    lock_guard<mutex> guard(queueMutex); //unhandled exception!
}

我认为这只是糟糕的设计,而不是 std::mutex 的问题。 - AndyG
我同意你的看法,@AndyG。 - Constantin Galbenu
在这种情况下使用递归互斥锁。 - Marek Waszkiewicz

0
不要使用微软的std::mutex(或recursive_mutex,...)!!! 我也遇到了类似的问题。我正在使用VS2012。
如果您在dll中使用std::mutex,那么您不能卸载此dll。因为你会得到一个未定义的行为。(在我的情况下是0xC0000005:访问位置出错....) 或者您无法卸载dll。(可能“runtime”增加了加载计数器。Double FreeLibrary()卸载了dll)

0
我遇到了同样的问题,解决方法是...进行完整的手动清理和重建!确保删除所有.obj、.dll、.lib文件。

你很幸运,但这并没有帮到我。 - Michel de Ruiter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接