在Python中调用exit()时,C ++析构函数中的互斥锁会导致异常

时间:2017-06-14 21:49:28

标签: python c++ multithreading locking mutex

我有一个应用程序,它使用一个处理队列中作业的类来加载DLL。为了保证线程安全,每当修改队列时都会锁定互斥锁。当应用程序退出并调用析构函数时,会锁定互斥锁以清除队列。

但是,当我在Python中加载此DLL时,创建该对象的实例,并调用exit()(在Python中)当互斥锁尝试锁定时抛出异常:

  

Microsoft Visual Studio C运行时库在python.exe中检测到致命错误。

我已经将析构函数简化为仅在本地创建互斥锁并尝试锁定它,并且仍然可以重现该问题:

QueueHandler::~QueueHandler(void)
{
    mutex mut; // in reality, this is a member of the class and there are actual operations between lock and unlock
    mut.lock(); // exception here
    mut.unlock();
}

如果我使用未修改的代码并简单地删除队列操作周围的锁,它就可以正常工作。

以下是调用堆栈中看似相关的部分:

KernelBase.dll!RaiseException() Unknown
msvcr120.dll!_CxxThrowException(void * pExceptionObject, const _s__ThrowInfo * pThrowInfo) Line 154 C++
msvcr120.dll!Concurrency::details::SchedulerBase::SchedulerBase(const Concurrency::SchedulerPolicy & policy) Line 149   C++
msvcr120.dll!Concurrency::details:: SchedulerBase::CreateWithoutInitializing(const Concurrency::SchedulerPolicy & policy) Line 285  C++
msvcr120.dll!Concurrency::details:: SchedulerBase::CreateContextFromDefaultScheduler() Line 571 C++
msvcr120.dll!Concurrency::details::SchedulerBase::CurrentContext() Line 404 C++
[Inline Frame] msvcr120.dll!Concurrency::details::LockQueueNode::{ctor (unsigned int) Line 619  C++
msvcr120.dll!Concurrency::critical_section::lock() Line 1031    C++
msvcp120.dll!mtx_do_lock(_Mtx_internal_imp_t * * mtx, const xtime * target) Line 67 C++
--> MyApplication.dll!QueueHandler::~QueueHandler() Line 106    C++
MyApplication.dll!_CRT_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 416  C
MyApplication.dll!__DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 522    C
ntdll.dll!LdrShutdownProcess()  Unknown
ntdll.dll!RtlExitUserProcess()  Unknown
msvcr100.dll!doexit(int code, int quick, int retcaller) Line 621    C
python27.dll!000000001e13be65() Unknown
...
python27.dll!000000001e043494() Unknown
python.exe!000000001d00119e()   Unknown

问题:

  1. 如果此代码在我正常退出应用程序时工作(关闭GUI),那么当我从Pyton exit()获取时,为什么会有所不同?
  2. 是否有更正确的"退出Python的方法?
  3. 这可能与正在使用的互斥锁/锁的类型有关吗?
  4. 我是否甚至需要在析构函数中使用队列操作锁定该部分?或者可以在没有锁定的情况下删除队列中的对象吗?
  5. 编辑: MCVE: QueueHandlerApp - 运行应用或运行script.py来演示此问题。

1 个答案:

答案 0 :(得分:0)

  

有些人在遇到问题时会想,"我知道,我会用   懒惰的初始化。"现在他们有两个问题。

这是std::mutex的MSVC实施中的错误。在MSVC14 std::mutexstd::condition_variable之前,懒惰地执行一些内部初始化。仅这一点,但由于how modules are deinitialized on Windows而变得更糟。

该错误已在MSVC14中修复(Visual Studio 2015) - std::mutex被重写为在内部使用SRWLockSRWLock是一个简单的原语,没有其他依赖项。它仅依赖于原子指令和Keyed Events系统调用。由于内核与用户空间隔离,SRWLock无论在何处使用都应该无法正常工作。

好像您正在使用MSVC12(Visual Studio 2013)。您应该切换到MSVC14(Visual Studio 2015)或使用Boost.Thread。

MSVC12及更早版本的std::mutex实际上存在很多问题。有些与CRT中使用的实际实现有关,其他(如我所见)是由Windows 7中的错误引起的,并且已在Windows 8中修复。