title: C# - Thread.Join(Milliseconds) and the finally block
body: <p>I am using .NET 2.0 and the finally block does not seem to be getting executed if the Thread times out.  For example, if I see the message "Child Thread Timed Out...", I will not see the message "Finally block started...".  This means that the database objects (Oracle.DataAccess) may not be getting cleaned up properly.  Is there a way to force the cleanup inside the child thread, or should the cleanup be moved to the main thread and pass in the database objects to the child thread?</p>
  
  <pre><code>   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 &lt;= 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 &gt;= 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);
          }
      }
  </code></pre>