【问题标题】:Unhandled exception when using std::mutex instead of boost::mutex使用 std::mutex 而不是 boost::mutex 时未处理的异常
【发布时间】:2015-03-04 05:21:49
【问题描述】:

我尝试摆脱代码中的一些 boost 依赖项,转而使用新的 C++11 功能 (Visual Studio 2013)。

在我的一个组件中,我使用了boost::mutexboost::lock_guard<boost::mutex>,一切正常。当我将std::mutexstd::lock_guard<std::mutex> 一起使用时,从main() 返回时出现以下错误。

GrabberTester.exe 中 0x7721E3BE (ntdll.dll) 处的未处理异常:0xC0000005:访问冲突读取位置 0xA6A6B491。

实际项目相当复杂,因此很难提供完整的工作代码示例来重现此问题。在我的实际项目中,互斥锁用于在运行时加载的共享库中(但在我从main() 返回时应该已经卸载了)。

我的问题是:

  • boost::mutexstd::mutex 的设计行为是否完全相同?
  • 如果不是,有什么区别?使用std::mutex 而不是boost::mutex 时需要注意什么?
  • 在共享库中,我使用boost::thread 框架创建线程。难道std::mutex只能和std::threads一起使用,和boost::threads不兼容?

编辑:

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

编辑 2:

应用程序和 DLL 都是使用 v120 工具集在调试配置中全新构建的,并与运行时库 (/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 中的一个错误,只有在这些非常特殊的条件下才能观察到。所以我在这个问题中添加了 openni 标签。但问题仍然存在:为什么它适用于 boost::mutex 而不适用于 std::mutex

【问题讨论】:

  • 就我而言,stl 和 boost 实现之间没有显着差异。我见过互斥锁这样做的唯一原因是当一个线程锁定而另一个尝试解锁时。我会确保我对所有可能的实例都使用 lock_guards,并通过大量同步调试输出调查所有其他用途。
  • 我知道标准线程工具的一些 VS2013 实现在各个方面都存在缺陷。不知道mutex 是否是其中的一部分。
  • 你有没有机会以某种方式混合不同的运行时版本?
  • 或者没有进行完整的重建? (即一些目标文件、DLL 和其他仍在使用旧代码的东西)
  • 至少发布崩溃的堆栈跟踪。

标签: c++11 mutex openni unhandled-exception boost-mutex


【解决方案1】:

问题很可能是静态初始化地狱,我最近经历了几乎同样的事情。以下是正在发生的事情:

  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。当对象在不同的​​编译单元中定义(即在不同的文件中定义)时,问题会更加严重。

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

【讨论】:

  • “当对象在不同的​​编译单元中定义时,问题会更加严重” 除非它们在不同的编译单元中定义,否则问题甚至不应该存在。单个翻译单元内的初始化顺序是明确定义的。此外,std::mutex 的构造函数是 constexpr,因此全局 std::mutex 应在任何需要动态初始化的全局对象之前初始化,因此应在这些对象之后销毁。我不认为微软的实现使用了constexpr,所以不符合这个要求。
【解决方案2】:

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

mutex queueMutex;

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

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

【讨论】:

  • 我认为这只是糟糕的设计,std::mutex 没有问题
  • 我同意你的看法,@AndyG
  • 在这种情况下使用recursive_mutex
【解决方案3】:

我添加了同样的问题,解决的是......完全手动清理+重建!验证删除所有 .obj、.dll、.lib 文件。

【讨论】:

  • 你很幸运。至少对我没有帮助。
【解决方案4】:

不要使用来自 Mircosoft 的 std::mutex(或 recursive_mutex,...)!!!! 我有类似的问题。我正在使用 VS2012。

如果您在 dll 中使用 std::mutex,则不得卸载此 dll。因为你得到一个未定义的行为。 (在我的情况下为 0xC0000005:访问冲突读取位置 ....)。 或者你不能卸载dll。 (可以“?runtime?”增加负载计数器。Double FreeLibrary() 卸载 dll)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-29
    • 2017-03-28
    • 2012-12-27
    • 1970-01-01
    • 1970-01-01
    • 2012-08-02
    • 2011-12-10
    相关资源
    最近更新 更多