2017-07-11 48 views
12

最近在CodeReview.SE上,我碰到了an answer,它談到了一種叫做「異常嘔吐」的技術。顯然這個技巧用於利用異常必須以線程安全的方式實現,而不管編譯器是否支持thread_local變量。什麼是「異常嘔吐」?

我粘貼下面這個答案的一部分:

有現有的技術,它是沒有什麼不同被稱爲「異常嘔吐」。注意:

void f(void(*p)()) { 
    p(); 
} 
template<typename F> void real_f(F func) { 
    try { 
     throw func; 
    } catch(...) { 
     f([] { 
      try { 
       throw; 
      } catch(F func) { 
       func(); 
      } 
     }); 
    } 
} 

這侵犯的事實,編譯器必須用作例外存儲複雜的對象提供了一個線程本地棧,無論其用於其他線程局部功能的支持,因此享有很大廣泛的編譯器的支持。最明顯的缺點是a)太糟糕了,b)僅限於堆棧語義。

我的問題是,這個技巧究竟是如何工作的,它是「安全的」?

+2

價值的「catch」關係到我一點,還是這個高於我的薪酬等級的東西? – Bathsheba

+0

@Bathsheba這不是我的代碼,我不知道它是如何工作的。這就是爲什麼我在這裏問); –

+0

我明白這一點;我的評論更針對能夠解決這個問題的人 - 我不是其中之一。 – Bathsheba

回答

5

該技術依賴於必須以線程安全的方式實現異常以使異常在多線程應用程序中可用的事實。甚至在線程成爲C++標準的一部分之前,C++-11編譯器也支持線程安全異常。

每個線程獨立於其它線程throw/catch例外,通過使用特定的線程存儲器,用於存儲異常。沒有參數的throw會重新拋出存儲在該線程專有存儲中的當前異常。該異常存儲用於存儲具有捕獲參數的函數(有狀態的lambda或任何其他可調用的函數)。

該技術的缺點是拋出異常通常涉及內存分配,因此它會增加調用的開銷。


來實現這一點的另一個方法是使用非便攜,但受到廣泛支持__thread存儲說明符。這樣可以避免動態內存分配的開銷:

void f(void(*p)()) { // The C-style function. 
    p(); 
} 

__thread void(*function)(); 

template<class Function> 
void adapter() { 
    (*reinterpret_cast<Function*>(function))(); 
} 

template<typename F> 
void invoke_f(F const& func) { 
    function = reinterpret_cast<void(*)()>(&func); 
    f(adapter<F const>); 
} 

int main(int ac, char**) { 
    invoke_f([ac]{ std::cout << ac << '\n'; }); 
}