线程中止叶僵尸交易和破碎的SqlConnection [英] Thread abort leaves zombie transactions and broken SqlConnection

查看:214
本文介绍了线程中止叶僵尸交易和破碎的SqlConnection的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我觉得这种行为不应该发生。这里的情景:

I feel like this behavior should not be happening. Here's the scenario:

  1. 开始一个长期运行的SQL事务。

  1. Start a long-running sql transaction.

这运行的SQL命令中的线程 被中止(不是由我们code!)

The thread that ran the sql command gets aborted (not by our code!)

在所管理的线程返回 code,关闭SqlConnection的状态 封闭 - 但是成交 在SQL Server上仍处于打开状态。

When the thread returns to managed code, the SqlConnection's state is "Closed" - but the transaction is still open on the sql server.

的SQLConnection可以重新开业, 你可以尝试调用回滚上 交易,但它没有 效果(不,我希望这种行为。问题的关键是没有办法来访问数据库的事务,并回滚。)

The SQLConnection can be re-opened, and you can try to call rollback on the transaction, but it has no effect (not that I would expect this behavior. The point is there is no way to access the transaction on the db and roll it back.)

这个问题很简单,即该交易不正确清理当线程中止。这是与.net 1.1,2.0和2.0 SP1中的问题。我们正在运行.NET 3.5 SP1。

The issue is simply that the transaction is not cleaned up properly when the thread aborts. This was a problem with .Net 1.1, 2.0 and 2.0 SP1. We are running .Net 3.5 SP1.

下面是说明问题的示例程序。

Here is a sample program that illustrates the issue.

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();
            }
        }
    }
}

有一个 Microsoft修补针对.Net2.0 SP1这应该解决这个,但我们显然有较新的DLL的(.NET 3.5 SP1)不符合在此修补程序中列出的版本号。

There is a Microsoft Hotfix targeted at .Net2.0 SP1 that was supposed to address this, but we obviously have newer DLL's (.Net 3.5 SP1) that don't match the version numbers listed in this hotfix.

任何人都可以解释这种现象,为什么ThreadAbort是的还是的不清理SQL事务是否正确?是否.NET 3.5 SP1不包含此修复程序,或者是这种行为在技术上是正确的?

Can anyone explain this behavior, and why the ThreadAbort is still not cleaning up the sql transaction properly? Does .Net 3.5 SP1 not include this hotfix, or is this behavior that is technically correct?

推荐答案

这是微软的MARS实现中的错误。在连接字符串中禁用MARS会让问题消失。

This is a bug in Microsoft's MARS implementation. Disabling MARS in your connection string will make the problem go away.

如果您需要MARS和舒适让您的应用程序依赖于另一家公司的内部实现,熟悉的 http://dotnet.sys-con.com/node/39040 ,打破了.net反射,并期待在连接池类。你必须保存前DbConnectionInternal属性的副本故障发生。后来,使用反射来引用传递给内部池类释放方法。这将停止从缠绵4:00您的连接 - 7:40分

If you require MARS, and are comfortable making your application dependent on another company's internal implementation, familiarize yourself with http://dotnet.sys-con.com/node/39040, break out .NET Reflector, and look at the connection and pool classes. You have to store a copy of the DbConnectionInternal property before the failure occurs. Later, use reflection to pass the reference to a deallocation method in the internal pooling class. This will stop your connection from lingering for 4:00 - 7:40 minutes.

有肯定其他的方法来强制连接出池,并进行处理。短距离微软的修补程序,虽然,反射似乎是必要的。在ADO.NET API中的公共方法似乎并没有帮助。

There are surely other ways to force the connection out of the pool and to be disposed. Short of a hotfix from Microsoft, though, reflection seems to be necessary. The public methods in the ADO.NET API don't seem to help.

这篇关于线程中止叶僵尸交易和破碎的SqlConnection的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆