1. 程式人生 > >C# 深度剖析try catch finally及其效能影響

C# 深度剖析try catch finally及其效能影響

  關於try-catch-finally的使用本文不做探討,詳見try-catch參考
  本文想真正剖析的是在程式碼中使用try-catch-finally塊對於效能的影響。很多程式設計師認為:只要沒有異常丟擲,try就沒有額外的效能開銷。為此,我們先來看微軟官方給出的解釋:

Throw Fewer Exceptions

Throwing exceptions can be very expensive, so make sure that you don’t throw a lot of them. Use Perfmon to see how many exceptions your application is throwing. It may surprise you to find that certain areas of your application throw more exceptions than you expected. For better granularity, you can also check the exception number programmatically by using Performance Counters.

MSDN

   上述第一句便講明瞭丟擲異常開銷非常大(相對而言),所以不要過多的在程式中使用它們。如果不能理解這句話,請嘗試執行以下程式碼:
  

public static void Main(string[] args){
  int j = 0;
  for(int i = 0; i < 10000; i++){
    try{   
      j = i;
      throw new System.Exception();
    } catch {}
  }
  System.Console.Write(j);
  return;   
}

  上面程式碼片中有一個迴圈,它執行10000次try-catch塊,丟擲10000個異常,在每次迴圈檢測到異常後,CLR會自上而下搜尋catch塊的程式碼,並通過異常過濾器篩選對應的異常,如果沒有找到,那麼CLR將沿著呼叫堆疊,向更高層搜尋匹配的異常,如果已到堆疊頂部依然沒有找到對應的異常,就會丟擲未處理的異常了,這時catch塊中的程式碼並不會被執行。所以距離try最近的catch塊將最先被遍歷到。
  在stackoverflow上有關於例項更為詳細的程式碼,詳情參閱:

try-catch塊效能開銷例項
  在基於X86架構的.NET2.0中,即時(just-in-time)編譯器禁用最優化會影響CIL語言對於受保護塊的讀寫操作,由此可見,即使沒有異常丟擲,try-catch塊也會有一定的效能開銷,看下面的例子:
  

    int count = 1;
    SomeMethod();
    count++;
    SomeOtherMethod();
    count++;
    Console.WriteLine(count);

  X86即時編譯器會將其優化為以下形式:

    SomeMethod();
    SomeOtherMethod()
; Console.WriteLine(3);

  最終結果雖然一致,但其副作用卻不相同(這種影響一般只能在偵錯程式中觀察到)。
通過如下方式把程式碼放在try語句中:

    int count = 1;
    try
    {
        SomeMethod();
        count++;
        SomeOtherMethod();
        count++;
    }
    finally
    {
        Console.WriteLine(count);
    }

  變數count的值初始為1,並在SomeMethod()方法呼叫後變為2,在SomeOtherMethod()方法呼叫後變為3,最終的輸出結果也會是3,在其過程中有兩次增量操作是額外的開銷浪費。
以上幾個例子告訴我們寫try-catch語句的時候一定要注意以下幾點:

  1. 儘量給CLR一個明確的異常資訊,不要使用Exception去過濾異常
  2. 不要輕易將try-catch寫在迴圈中
  3. try儘量少的程式碼,如果有必要可以使用多個catch塊,並且將最有可能丟擲的異常型別,書寫在距離try最近的位置