2017-01-09 53 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