異常傳輸

異常傳輸

異常傳輸通常是指程式在運行時,從一個執行緒向另一個執行緒的通信過程發生異常。

基本介紹

  • 中文名:異常傳輸
  • 外文名:Abnormal transmission
  • 學科:計算機科學
  • 影響:阻礙執行緒運行
概念,解決方案,異常處理模型和編譯器選項,示例,

概念

異常傳輸通常是指程式在運行時,從一個執行緒向另一個執行緒的通信過程發生異常。通過傳輸異常,你可以在一個執行緒中捕獲異常,然後使該異常看似是在另一個執行緒中引發的。 例如,你可以使用該功能編寫多執行緒應用程式,其中主執行緒將處理其輔助執行緒引發的所有異常。 傳輸異常對創建並行編程庫或系統的開發人員最有用處。 為了實現傳輸異常,Visual c++提供了 exception_ptr 類型和current_exception,rethrow_exception和make_exception_ptr 函式。

解決方案

假設你要創建能伸縮以處理可變工作負荷的應用程式。 為了實現此目標,你要設計一個多執行緒應用程式,其中初始的主執行緒會創建所需數量的輔助執行緒,以完成該工作。 輔助執行緒可幫助主執行緒管理資源、平衡負載和提高吞吐量。 通過分發工作,多執行緒應用程式的表現優於單執行緒應用程式。
但是,如果輔助執行緒引發異常,你希望主執行緒予以處理。 這是因為你希望你的應用程式無論有多少輔助執行緒,都能以一致統一的方式處理異常。
為處理先前的方案,C++ 標準支持線上程之間傳輸異常。 如果輔助執行緒引發異常,該異常會成為 當前異常。 拿現實世界打比方,當前異常就好比處於飛行狀態。 當前異常從引發之時到捕獲它的異常處理程式返回之時就處於飛行狀態。
輔助執行緒可在 catch 塊中捕獲當前異常,然後調用 current_exception 函式將該異常存儲在 exception_ptr 對象中。 exception_ptr 對象必須可用於輔助執行緒和主執行緒。 例如,exception_ptr 對象可以是全局變數,由 mutex 控制對它的訪問。 傳輸異常意味著一個執行緒中的異常可以轉換為可以由其他執行緒訪問窗體。
接下來,主執行緒調用 rethrow_exception 函式,該函式提取並繼而引發 exception_ptr 對象中的異常。 異常引發後,將成為主執行緒中的當前異常。 也就是說,該異常看起來源自主執行緒。
最後,主執行緒可以捕獲 catch 塊中的當前異常,然後進行處理或將其拋給更高級別的異常處理程式。 或者,主執行緒可以忽略該異常並允許該進程結束。
大多數應用程式不必線上程之間傳輸異常。 但是,該功能在並行計算系統中有用,是因為該系統可以在輔助執行緒、處理器或核心間分配工作。 在並行計算環境中,單個專用執行緒可以處理輔助執行緒中的所有異常,並可以為任何應用程式提供一致的異常處理模型。

異常處理模型和編譯器選項

你的應用程式的異常處理模型決定了它是否可以捕獲和傳輸異常。 Visual C++ 支持三種模型,這些模型可以處理 C++ 異常、結構化異常處理 (SEH) 異常和公共語言運行時 (CLR) 異常。 使用 /EH 和 /clr 編譯器選項可指定應用程式的異常處理模型。
只有編譯器選項和編程語句的以下組合可以傳輸異常。 其他組合要么不能捕獲異常,要么能捕獲但不能傳輸異常。
  • /EHa 編譯器選項和 catch 語句可以傳輸 SEH 和 c + + 異常。
  • /EHa, ,/EHs, ,和 /EHsc 編譯器選項和 catch 語句可以傳輸 c + + 異常。
  • /CLR 或 /CLR: pure 編譯器選項和 catch 語句可以傳輸 c + + 異常。 /CLR 編譯器選項暗含的規範 /EHa 選項。 請注意,編譯器不支持傳輸託管異常。 這是因為託管異常派生自 System.Exception 類, ,已經是可通過使用公共語言運行時的功能的執行緒間移動的對象。

示例

以下示例從一個執行緒向另一個執行緒傳輸標準 C++ 異常和自定義 C++ 異常。
// transport_exception.cpp
// compile with: /EHsc /MD
#include <windows.h>
#include <stdio.h>
#include <exception>
#include <stdexcept>
using namespace std;
// Define thread-specific information.
#define THREADCOUNT 2
exception_ptr aException[THREADCOUNT];
int aArg[THREADCOUNT];
DWORD WINAPI ThrowExceptions( LPVOID );
// Specify a user-defined, custom exception.
// As a best practice, derive your exception
// directly or indirectly from std::exception.
class myException : public std::exception {
};
int main()
{
HANDLE aThread[THREADCOUNT];
DWORD ThreadID;
// Create secondary threads.
for( int i=0; i < THREADCOUNT; i++ )
{
aArg[i] = i;
aThread[i] = CreateThread(
NULL, // Default security attributes.
0, // Default stack size.
(LPTHREAD_START_ROUTINE) ThrowExceptions,
(LPVOID) &aArg[i], // Thread function argument.
0, // Default creation flags.
&ThreadID); // Receives thread identifier.
if( aThread[i] == NULL )
{
printf("CreateThread error: %d\n", GetLastError());
return -1;
}
}
// Wait for all threads to terminate.
WaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);
// Close thread handles.
for( int i=0; i < THREADCOUNT; i++ ) {
CloseHandle(aThread[i]);
}
// Rethrow and catch the transported exceptions.
for ( int i = 0; i < THREADCOUNT; i++ ) {
try {
if (aException[i] == NULL) {
printf("exception_ptr %d: No exception was transported.\n", i);
}
else {
rethrow_exception( aException[i] );
}
}
catch( const invalid_argument & ) {
printf("exception_ptr %d: Caught an invalid_argument exception.\n", i);
}
catch( const myException & ) {
printf("exception_ptr %d: Caught a myException exception.\n", i);
}
}
}
// Each thread throws an exception depending on its thread
// function argument, and then ends.
DWORD WINAPI ThrowExceptions( LPVOID lpParam )
{
int x = *((int*)lpParam);
if (x == 0) {
try {
// Standard C++ exception.
// This example explicitly throws invalid_argument exception.
// In practice, your application performs an operation that
// implicitly throws an exception.
throw invalid_argument("A C++ exception.");
}
catch ( const invalid_argument & ) {
aException[x] = current_exception();
}
}
else {
// User-defined exception.
aException[x] = make_exception_ptr( myException() );
}
return TRUE;
}
Output
exception_ptr 0: Caught an invalid_argument exception.
exception_ptr 1: Caught a myException exception.

相關詞條

熱門詞條

聯絡我們