2010-10-29 75 views
1

我使用.NET 2.0和finally塊似乎沒有被得到執行,如果出線倍。例如,如果我看到消息「Child Thread Timed Out ...」,我將看不到消息「Finally block started ...」。這意味着數據庫對象(Oracle.DataAccess)可能無法正確清理。有沒有辦法在子線程中強制清理,還是應該將清理移動到主線程並將數據庫對象傳遞給子線程?C# - 的Thread.join(毫秒)和finally塊

private void runThread(string strSP, object objThreadParameter) 
    { 
     try 
     { 
      bool blnThreadCompletedOK = true; 

      Thread threadHelper = new Thread(getData); 
      threadHelper.Start(objThreadParameter); 

      // Wait for called thread. 
      blnThreadCompletedOK = threadHelper.Join(THREAD_TIMEOUT); 
      if (blnThreadCompletedOK) 
      { 
       // Thread has completed and should have stopped running. 
       // i.e. the thread has processed normally or an exception has been copied to the objExceptionThread object. 
       if (objExceptionThread != null) 
       { 
        throw objExceptionThread; 
       } 
      } 
      else 
      { 
       System.Diagnostics.EventLog.WriteEntry("Main thread", "Child Thread Timed Out...", System.Diagnostics.EventLogEntryType.Warning); 

       // Main thread has timed out waiting for the child thread. Likely the child thread is still running. 
       if (threadHelper.IsAlive) 
       { 
        threadHelper.Abort(); // This will trigger the exception handling in the child thread and cause the finally 
              // block to be executed. 
       } 
       throw (new Exception("The call to " + strSP + "() timed out as it exceeded " + (THREAD_TIMEOUT/1000).ToString() + " seconds")); 
      } 
     } 
     catch (Exception exc) 
     { 
      throw new PivotalApplicationException(exc.Message, exc, mrsysSystem); 
     } 
    } 


    private void getData(object objThreadParameter) 
    { 
     OracleCommand oraCmd = null; 
     OracleConnection oraConn = null; 
     OracleDataReader dr = null; 

     try 
     {    
      // Initialization. 
      int intMAX_RETRIES = 20;  // Maximum number of retries. 
      int intRETRY_DROP_POOL = 5; // At some point, if connections are still failing, try clearing the pool. 

      // Other initialization stuff... 

      // Now execute the SP. 
      for (int i = 1; i <= intMAX_RETRIES; i++) 
      { 
       try 
       { 
        try 
        { 
         // Setup Oracle connection and initialize Oracle command object. 
         getOracleConnection(out oraConn, connString); 
        } 
        catch (Exception exc) 
        { 
         throw new Exception("Error in getData() setting up connection - " + exc.Message); 
        } 

        try 
        { 
         oraCmd = new OracleCommand(strSP, oraConn); 
         setupCommand (out oraCmd); 
        } 
        catch (Exception exc) 
        { 
         throw new Exception("Error in getData() setting up parameters - " + exc.Message); 
        } 

        try 
        { 
         dr = oraCmd.ExecuteReader(); 
         break; // Success, so, leave the for loop. 
        } 
        catch (Exception exc) 
        { 
         throw new Exception("Error in getData() executing command.\n\n" + strParametersMsg + " \n\n" + exc.Message); 
        } 
       } 
       catch (Exception excInner) 
       { 

        if (i >= intMAX_RETRIES) 
        { 
         throw new Exception(excInner.Message); 
        } 
        else 
        { 
         // Cleanup oraCmd, oraConn, oraDr... 
        } 
       } 
      } 

      try 
      { 
       // Process results... 
      } 
      catch (Exception exc) 
      { 
       throw new Exception("Error in getData() processing results - " + exc.Message); 
      } 

      // Now set the variables that are shared between the Main thread and this thread... 

     } 
     catch (Exception exc) 
     { 
      logMessage(exc.Source + " " + exc.Message); 
      objExceptionThread = exc; // Initialize exception in Main Thread... 
     } 
     finally 
     { 
      System.Diagnostics.EventLog.WriteEntry("Child Thread", "Finally block started...", System.Diagnostics.EventLogEntryType.Warning); 

      // With .NET 2.0 and later, the finally block should always be executed correctly for a Thread.Abort() 
      if (!(dr == null)) 
      { 
       dr.Dispose(); 
      } 
      if (!(oraCmd == null)) 
      { 
       oraCmd.Dispose(); 
      } 
      if (!(oraConn == null)) 
      { 
       oraConn.Close(); 
       oraConn.Dispose(); 
      } 

      System.Diagnostics.EventLog.WriteEntry("Child Thread", "Finally block completed...", System.Diagnostics.EventLogEntryType.Warning); 
     } 
    } 

回答

2

您應該只中止一個線程,如果該線程沒有任何重要性再做。在其他情況下,我建議設置一些通知(即在線程上設置一個布爾屬性),使線程正常關閉。話雖如此根據documentation finally塊被在執行.NET 2.0:

當呼叫到Abort方法制成銷燬線程,公共語言運行時會引發一個ThreadAbortException。 ThreadAbortException是一個可以被捕獲的特殊異常,但它會在catch塊的末尾再次自動提升。當引發此異常時,運行時會在結束線程之前執行所有finally塊。

我最好的猜測是,主線程在你的線程的finally塊有機會執行之前退出。嘗試在放棄線程後放入Thread.Sleep以查看是否改變了行爲。

編輯: 我寫與.NET 2.0產生以下輸出示出該最後塊執行一個簡單的例子。

活蹦亂跳

活蹦亂跳

活蹦亂跳

異常

最後

class ThreadTest 
{ 
    public ThreadTest() { } 

    public void test() 
    { 
     try 
     { 
      while (true) 
      { 
       Console.WriteLine("Alive and kicking"); 
       Thread.Sleep(2000); 
      } 
     } 

     catch (Exception ex) 
     { 
      Console.WriteLine("Exception"); 
     } 

     finally 
     { 
      Console.WriteLine("Finally"); 

     } 
    } 
} 
class Program 
{ 
    static void Main(string[] args) 
    { 
     ThreadTest myThreadTest = new ThreadTest(); 
     Thread myThread = new Thread(new ThreadStart(myThreadTest.test)); 
     myThread.Start(); 
     Thread.Sleep(5000); 
     bool status = myThread.Join(1000); 
     if (myThread.IsAlive) 
     { 
      myThread.Abort(); 
     } 
     Thread.Sleep(5000); 
    } 
} 
1

您只能在線程處於託管代碼時中斷線程。如果是在本機代碼,然後運行調度ThreadAbortException要在本地代碼返回時拋出。之後將執行finally塊。在本機函數返回並繼續執行託管執行之前,finally塊不會運行。

如果你的問題是,本機代碼被掛起,那麼finally塊無法運行。

0

感謝 - 這個問題似乎是時機之一。如果我重複檢查threadHelper.IsAlive屬性並保持主線程運行,finally塊將執行。所以,我認爲代碼掛在dr = oraCmd.ExecuteReader(); 的的Thread.join()返回時,試圖中止() - 但不能在那個時候,然後主線程結束,因此在子線程也被打死。我認爲這確實留下了一個開放的連接。

ODP.NET應該是一個託管數據提供者(http://wiki.oracle.com)。com/page/Oracle + Data + Provider + for + .Net),它也有一個命令超時屬性。

「的CommandTimeout指定的秒的命令被允許與一個異常終止執行之前執行的數目」。

我將進一步調查爲什麼將CommandTimeout似乎沒有得到尊重,如果還是失敗,我可能會嘗試結束每誰似乎都在本書的人應用程序域。

http://www.albahari.com/threading/part4.aspx#_Aborting_Threads

感謝您的幫助!

+0

事實證明,最終塊的線程上執行 - 甲骨文SP只花了這麼長的時間> 20分鐘,我從來沒有等待那麼長的時間查看事件日誌消息出現。 – JulianM 2010-11-10 00:09:33