2017-01-09 119 views
2

我在Visual Studio中运行该代码2013(调试配置):Visual Studio的多线程调试

#include <thread> 
#include <stdexcept> 

void c() { 
    /* breakpoint here*/ throw std::runtime_error("error"); 
} 

void b() { 
    c(); 
} 

void a() { 

    b(); 

} 

int main(int argc, char** argv) { 
    std::thread thr(a); 

    if (thr.joinable()) thr.join(); 

    return 0; 
} 

执行暂停在断点处,我看到的调用堆栈:

> DemoProj.exe!c() Line 5 C++ 
DemoProj.exe!b() Line 10 C++ 
DemoProj.exe!a() Line 16 C++ 
[External Code] 

这是伟大的!我可以确切地看到我在执行过程中的位置。

之后,我走了一步(F10)只是为了执行这个异常投掷线。 当然,除了得到投掷,但现在我的调用堆栈看起来是这样的:

> msvcp120d.dll!_Call_func$catch$0() Line 30 C++ 
msvcr120d.dll!_CallSettingFrame() Line 51 Unknown 
msvcr120d.dll!__CxxCallCatchBlock(_EXCEPTION_RECORD * pExcept) Line 1281 C++ 
ntdll.dll!RcConsolidateFrames() Unknown 
msvcp120d.dll!_Call_func(void * _Data) Line 28 C++ 
msvcr120d.dll!_callthreadstartex() Line 376 C 
msvcr120d.dll!_threadstartex(void * ptd) Line 359 C 
kernel32.dll!BaseThreadInitThunk() Unknown 
ntdll.dll!RtlUserThreadStart() Unknown 

这使我调试没用。 线

/* breakpoint here*/ throw std::runtime_error("error"); 

只是这里这个简单的例子。在真实的项目中,我不知道代码会在哪里破坏。如果Visual Studio可以在发生错误的确切行上停止执行,那将会非常有帮助。但是相反,如果在我的主线程之外的任何地方抛出一个异常,我只能得到这些系统调用,并且永远无法弄清楚我的代码中的哪一行导致崩溃。

任何帮助?我觉得这个问题跟this one差不多。 想法是捕获侧线程中的异常并将它们传递给主线程,以便主线程可以重新抛出它们。没有更优雅的解决方案吗?

+2

异常已被捕获后,您无法做任何事情。堆栈已经被解开。但是,在实际抛出任何东西之前,您可以编写自己的异常类来转储堆栈(读取StackWalk64)。 https://msdn.microsoft.com/en-us/library/windows/desktop/ms680650(v=vs.85).aspx –

+1

调试器通常停止在未处理的异常。麻烦的是,它不是无法处理的。您将看到实现未处理的异常调用terminate()的要求的代码。这是一个std :: thread危险。您必须使用调试>例外>调试这一个,在C++异常中勾选Thrown复选框。 –

+1

你想在调试器中看到什么?鉴于F10的步骤,你就完成了。你在问如何在另一个玩家身上抓住这个模样? – doctorlove

回答

3

如果Visual Studio可以在发生错误的确切行上停止执行,将会非常有帮助。

Visual Studio有一个选项可以在引发异常时自动中断。在Vs 2015中,它是Debug - > Windows - > Exception Settings。勾选Break When Thrown标题下的C++ Exceptions复选框。

这对计算出哪些代码引发异常很有帮助。缺点是,如果你有代码例行地抛出并处理异常,你会在调试器中产生很多不希望的中断。

+0

是的,这是我正在寻找的答案。 MRB已经在评论中回答了它。但我不能接受评论作为答案... – ancajic

0

为什么不使用基于任务的编程。 无论何时您想在单独的线程中运行代码,而不是创建并运行新线程,只需使用线程池中已存在的线程即可。

C++ 11提供std::async,它的返回类型是std::future

std::future<void> future = std::async(std::launch::async, []{ a(); }); 

在其他线程抛出的每一个异常会被捕获并存储在std::future对象,如果你对std::futureget将抛出。

如果你想有堆栈跟踪,你可以编写一个自定义异常,它使用特定于平台的函数捕获堆栈函数名称并将它们存储在自身中。

+0

我不是那个低调的人,我希望能得到一个解释。 – ancajic

+0

@ancajic据我所知,你需要一种在并发环境中进行异常日志记录的方式,我认为这是一种做法。 – MRB