我的代码中很少出现问题,其中触发了断言,涉及Boost.Thread库 . 我无法使用一个独立的示例重现此问题,我真的不知道是什么导致它,所以很难提供一个示例案例 . 我希望任何熟悉boost.thread内部的人都可以提供帮助 .
这就是我所知道的:
-
声明
boost::lock_guard<boost::recursive_mutex>
(或unique_lock和普通非递归互斥锁的变体)时会出现问题 . -
它发生在Boost.Asio的处理函数中 . 在堆栈上是执行
io_service::run
的线程,一堆调用Asio回调函数的 Binders ,然后是我的回调函数(由async_write调用触发) . 该函数的第一行是导致问题的lock_guard<>
的声明 . -
this
我的函数内部有效,并且尚未删除或类似的东西 . 调试器显示它指向有效数据 . 在我的handle_write
函数中锁定的互斥锁也可以防止删除处理函数使用的内存 . -
这样可以正常工作,我会说10,000个中有9,999次,并且正在进行大量的多线程使用 . 如果我将应用程序使用的线程数减少到只处理一个处理Asio run()调用的线程和一个主UI线程,则问题会以相同的频率发生 .
-
我的代码的第一行调用互斥锁的
lock()
方法(在boost::unique_lock<>
的ctor中),然后调用boost::detail::basic_recursive_mutex_impl
中的lock()
,它调用boost::detail::basic_timed_mutex
的lock()
方法 . -
在Boost 1.46中,断言(
BOOST_VERIFY
)位于basic_timed_mutex.hpp的第78行,它调用win32 :: WaitForSingleObject:
do
{
BOOST_VERIFY(win32::WaitForSingleObject(
sem,::boost::detail::win32::infinite)==0);
clear_waiting_and_try_lock(old_count);
lock_acquired=!(old_count&lock_flag_value);
}
while(!lock_acquired);
-
当Boost.Thread代码等待获取互斥锁上的锁(此代码路径使用
WaitForSingleObject
)时,没有其他线程持有互斥锁(至少在断言发生时,可以检查)在调试器中) . 这很奇怪,因为它应该能够获得锁定,而不必等待另一个线程放弃控制 . -
事情看起来很奇怪,检查互斥体的成员 . 这些是所有本地和成员变量的值(除非另有说明,否则每次发生时它们都是相同的):
-
sem
- 0xdddddddddddddddd - 每次崩溃都是一样的 . -
lock_acquired
- false . -
old_count
- 0xdddddddddddddddd . -
this
- 看似有效,并且它的地址与持有它的对象(其中handle_write
是方法的对象)匹配 . 它似乎没有被删除或以任何方式搞乱 . -
this->active_count
- 一个负整数,我见过的范围介于-570000000和-580000000之间 . -
this->event
- 0xdddddddddddddddd .
遗憾的是,我无法看到 WaitForSingleObject
电话的结果 . API函数上的MSDN entry表示四种可能的返回类型,在这种情况下其中两种不可能 . 由于使用无效的事件句柄( sem
= 0xdddddddddddddddd
)调用 WaitForSingleObject
,我假设它返回 0xFFFFFFFF
并且GetLastError将指示已提供无效句柄 .
所以实际问题似乎是 basic_timed_mutex
的 get_event()
方法正在返回 0xdddddddddddddddd
. 但是, CreateEvent
( get_event()
最终使用)告诉我它返回一个事件的有效句柄,或 NULL
.
同样,这可能是我能提供的问题的最佳描述,因为它在该特定应用之外不可靠地再现 . 我希望有人对可能造成这种情况的想法有所了解!
1 回答
我想要对你的问题给出一个精确的答案是非常困难的,但似乎你有一个堆损坏问题,你是否尝试过使用正常的pageheap启用AppVerifier?如果您随后将一个调试器附加到进程并且存在堆损坏,那么当遇到损坏的堆块时,它肯定会中断,您甚至可以查看分配代码的callstack .
edit: 如果使用WinDbg你也可以在WaitForSingleObject(或任何其他函数)上放置一个条件断点,只有在调用失败时才会中断,然后检查最后一个错误,例如: bp kernel32!WaitForSingleObject "gu; .if(eax == 0) " - >这将告诉调试器在断点处i)运行到函数结束(gu)和ii)检查返回值(存储在EAX寄存器中)和继续执行(g)如果一切都很好 . 如果返回错误,您可以使用 !gle 扩展命令检查GetLastError()的值 .