16 Stimmen

Threadabbruch hinterlässt Zombie-Transaktionen und defekte SqlConnection

Ich habe das Gefühl, dass dieses Verhalten nicht vorkommen sollte. Hier ist das Szenario:

  1. Starten Sie eine langlaufende Sql-Transaktion.

  2. Der Thread, der den Sql-Befehl ausgeführt hat wird abgebrochen (nicht durch unseren Code!)

  3. Wenn der Thread zur verwalteten Code zurückkehrt, ist der Zustand der SqlConnection "Geschlossen" - aber die Transaktion ist auf dem SQL-Server noch offen.

  4. Die SQLConnection kann wieder geöffnet werden, und Sie können versuchen, ein Rollback für der Transaktion aufzurufen, aber das hat keine Wirkung (nicht, dass ich dieses Verhalten erwarten würde. Der Punkt ist, dass es keine Möglichkeit gibt, auf die Transaktion in der Datenbank zuzugreifen und sie zurückzusetzen).

Das Problem besteht einfach darin, dass die Transaktion nicht ordnungsgemäß bereinigt wird, wenn der Thread abbricht. Dies war ein Problem mit .Net 1.1, 2.0 und 2.0 SP1. Wir verwenden .Net 3.5 SP1.

Hier ist ein Beispielprogramm, das das Problem veranschaulicht.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Data.SqlClient;
using System.Threading;

namespace ConsoleApplication1
{
    class Run
    {
        static Thread transactionThread;

        public class ConnectionHolder : IDisposable
        {
            public void Dispose()
            {
            }

            public void executeLongTransaction()
            {
                Console.WriteLine("Starting a long running transaction.");
                using (SqlConnection _con = new SqlConnection("Data Source=<YourServer>;Initial Catalog=<YourDB>;Integrated Security=True;Persist Security Info=False;Max Pool Size=200;MultipleActiveResultSets=True;Connect Timeout=30;Application Name=ConsoleApplication1.vshost"))
                {
                    try
                    {
                        SqlTransaction trans = null;
                        trans = _con.BeginTransaction();

                        SqlCommand cmd = new SqlCommand("update <YourTable> set Name = 'XXX' where ID = @0; waitfor delay '00:00:05'", _con, trans);
                        cmd.Parameters.Add(new SqlParameter("0", 340));
                        cmd.ExecuteNonQuery();

                        cmd.Transaction.Commit();

                        Console.WriteLine("Finished the long running transaction.");
                    }
                    catch (ThreadAbortException tae)
                    {
                        Console.WriteLine("Thread - caught ThreadAbortException in executeLongTransaction - resetting.");
                        Console.WriteLine("Exception message: {0}", tae.Message);
                    }
                }
            }
        }

        static void killTransactionThread()
        {
            Thread.Sleep(2 * 1000);

            // We're not doing this anywhere in our real code.  This is for simulation
            // purposes only!
            transactionThread.Abort();

            Console.WriteLine("Killing the transaction thread...");
        }

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            using (var connectionHolder = new ConnectionHolder())
            {
                transactionThread = new Thread(connectionHolder.executeLongTransaction);
                transactionThread.Start();

                new Thread(killTransactionThread).Start();

                transactionThread.Join();

                Console.WriteLine("The transaction thread has died.  Please run 'select * from sysprocesses where open_tran > 0' now while this window remains open. \n\n");

                Console.Read();
            }
        }
    }
}

Es gibt eine Microsoft Hotfix für .Net2.0 SP1, der dieses Problem beheben sollte aber wir haben offensichtlich neuere DLLs (.Net 3.5 SP1), die nicht mit den in diesem Hotfix aufgeführten Versionsnummern übereinstimmen.

Kann jemand dieses Verhalten erklären, und warum die ThreadAbort ist immer noch die Sql-Transaktion nicht richtig bereinigt? Beinhaltet .Net 3.5 SP1 diesen Hotfix nicht, oder ist dieses Verhalten technisch korrekt?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X