没有用户代码的死锁
Deadlock with no user code
我在使用 std::thread、std::mutex、std::condition_variable 等的 C++ 程序中出现死锁
这本身并没有什么奇怪的,直到我查看进程中每个线程的堆栈:
8532 0 Main Thread Main Thread msvcr120.dll!Concurrency::details::ExternalContextBase::Block Normal
ntdll.dll!_ZwWaitForSingleObject@12()
KernelBase.dll!_WaitForSingleObjectEx@12()
kernel32.dll!_WaitForSingleObjectExImplementation@12()
msvcr120.dll!Concurrency::details::ExternalContextBase::Block() Line 145
ntdll.dll!_ZwQueryVirtualMemory@24()
kernel32.dll!_BasepFillUEFInfo@8()
ntdll.dll!_ZwQueryInformationProcess@20()
msvcr120.dll!_initterm(void (void) * * pfbegin, void (void) * * pfend) Line 954
-
6484 0 Worker Thread ntdll.dll!_TppWaiterpThread@4() ntdll.dll!_NtWaitForMultipleObjects@20 Normal
ntdll.dll!_NtWaitForMultipleObjects@20()
ntdll.dll!_TppWaiterpThread@4()
kernel32.dll!@BaseThreadInitThunk@12()
ntdll.dll!___RtlUserThreadStart@8()
ntdll.dll!__RtlUserThreadStart@8()
-
6296 0 Worker Thread msvcr120.dll!_threadstartex msvcr120.dll!Concurrency::details::ExternalContextBase::Block Normal
ntdll.dll!_ZwWaitForSingleObject@12()
KernelBase.dll!_WaitForSingleObjectEx@12()
kernel32.dll!_WaitForSingleObjectExImplementation@12()
msvcr120.dll!Concurrency::details::ExternalContextBase::Block() Line 145
msvcp120.dll!std::_Thrd_startX(struct _Thrd_imp_t *,unsigned int (*)(void *),void *)
msvcr120.dll!_callthreadstartex() Line 376
msvcr120.dll!_threadstartex(void * ptd) Line 354
kernel32.dll!@BaseThreadInitThunk@12()
ntdll.dll!___RtlUserThreadStart@8()
ntdll.dll!__RtlUserThreadStart@8()
None 个线程似乎正在执行我的代码,我知道我们已经进入了 main,因为程序在挂起之前已经做了一些事情。
我正在使用以下 class 与我的 std::thread 通信,以防我在那里犯了一些错误:
template <typename T>
class BlockingQueue
{
public:
BlockingQueue() : _active(true) {}
bool Get(T& out)
{
std::unique_lock<std::mutex> lock(_mutex);
_cv.wait(lock, [&](){ return !_queue.empty() || !_active; });
if (_queue.empty())
{
assert(!_active);
return false;
}
out = std::move(_queue.front());
_queue.pop();
return true;
}
void Put(const T& in)
{
{
std::unique_lock<std::mutex> lock(_mutex);
_queue.push(in);
}
_cv.notify_one();
}
void Put(T&& in)
{
{
std::unique_lock<std::mutex> lock(_mutex);
_queue.push(std::move(in));
}
_cv.notify_one();
}
void Finish()
{
{
std::unique_lock<std::mutex> lock(_mutex);
_active = false;
}
_cv.notify_all();
}
private:
bool _active;
std::mutex _mutex;
std::condition_variable _cv;
std::queue<T> _queue;
};
我现在有两个想法:
- Main 由于某种原因已经退出。这是一个 PoC,所以当出现错误时,我们会记录到 stdout 并调用 exit()(是的,我知道,这不是最好的,这是从另一个用 C++ 编写的 C 风格程序改编而来的)。我没有看到任何内容被记录到终端,但我想输出可能正在缓冲并且尚未写出?
- 调试器在某种程度上对我撒谎。通常它会在执行此操作时将
[frames below may be missing/incorrect]
放入堆栈跟踪中,但也许没有它也会发生。
原来我未能替换队列中的项目,导致我的线程在从队列中检索时死锁,这意味着调试器在骗我。 :(
我在使用 std::thread、std::mutex、std::condition_variable 等的 C++ 程序中出现死锁
这本身并没有什么奇怪的,直到我查看进程中每个线程的堆栈:
8532 0 Main Thread Main Thread msvcr120.dll!Concurrency::details::ExternalContextBase::Block Normal
ntdll.dll!_ZwWaitForSingleObject@12()
KernelBase.dll!_WaitForSingleObjectEx@12()
kernel32.dll!_WaitForSingleObjectExImplementation@12()
msvcr120.dll!Concurrency::details::ExternalContextBase::Block() Line 145
ntdll.dll!_ZwQueryVirtualMemory@24()
kernel32.dll!_BasepFillUEFInfo@8()
ntdll.dll!_ZwQueryInformationProcess@20()
msvcr120.dll!_initterm(void (void) * * pfbegin, void (void) * * pfend) Line 954
-
6484 0 Worker Thread ntdll.dll!_TppWaiterpThread@4() ntdll.dll!_NtWaitForMultipleObjects@20 Normal
ntdll.dll!_NtWaitForMultipleObjects@20()
ntdll.dll!_TppWaiterpThread@4()
kernel32.dll!@BaseThreadInitThunk@12()
ntdll.dll!___RtlUserThreadStart@8()
ntdll.dll!__RtlUserThreadStart@8()
-
6296 0 Worker Thread msvcr120.dll!_threadstartex msvcr120.dll!Concurrency::details::ExternalContextBase::Block Normal
ntdll.dll!_ZwWaitForSingleObject@12()
KernelBase.dll!_WaitForSingleObjectEx@12()
kernel32.dll!_WaitForSingleObjectExImplementation@12()
msvcr120.dll!Concurrency::details::ExternalContextBase::Block() Line 145
msvcp120.dll!std::_Thrd_startX(struct _Thrd_imp_t *,unsigned int (*)(void *),void *)
msvcr120.dll!_callthreadstartex() Line 376
msvcr120.dll!_threadstartex(void * ptd) Line 354
kernel32.dll!@BaseThreadInitThunk@12()
ntdll.dll!___RtlUserThreadStart@8()
ntdll.dll!__RtlUserThreadStart@8()
None 个线程似乎正在执行我的代码,我知道我们已经进入了 main,因为程序在挂起之前已经做了一些事情。
我正在使用以下 class 与我的 std::thread 通信,以防我在那里犯了一些错误:
template <typename T>
class BlockingQueue
{
public:
BlockingQueue() : _active(true) {}
bool Get(T& out)
{
std::unique_lock<std::mutex> lock(_mutex);
_cv.wait(lock, [&](){ return !_queue.empty() || !_active; });
if (_queue.empty())
{
assert(!_active);
return false;
}
out = std::move(_queue.front());
_queue.pop();
return true;
}
void Put(const T& in)
{
{
std::unique_lock<std::mutex> lock(_mutex);
_queue.push(in);
}
_cv.notify_one();
}
void Put(T&& in)
{
{
std::unique_lock<std::mutex> lock(_mutex);
_queue.push(std::move(in));
}
_cv.notify_one();
}
void Finish()
{
{
std::unique_lock<std::mutex> lock(_mutex);
_active = false;
}
_cv.notify_all();
}
private:
bool _active;
std::mutex _mutex;
std::condition_variable _cv;
std::queue<T> _queue;
};
我现在有两个想法:
- Main 由于某种原因已经退出。这是一个 PoC,所以当出现错误时,我们会记录到 stdout 并调用 exit()(是的,我知道,这不是最好的,这是从另一个用 C++ 编写的 C 风格程序改编而来的)。我没有看到任何内容被记录到终端,但我想输出可能正在缓冲并且尚未写出?
- 调试器在某种程度上对我撒谎。通常它会在执行此操作时将
[frames below may be missing/incorrect]
放入堆栈跟踪中,但也许没有它也会发生。
原来我未能替换队列中的项目,导致我的线程在从队列中检索时死锁,这意味着调试器在骗我。 :(