【发布时间】:2011-06-02 19:20:09
【问题描述】:
我觉得这种行为不应该发生。这是场景:
启动一个长时间运行的 sql 事务。
运行 sql 命令的线程 被中止(不是我们的代码!)
当线程返回托管 代码,SqlConnection 的状态是 “关闭” - 但交易是 仍然在 sql server 上打开。
SQLConnection 可以重新打开, 你可以尝试调用回滚 交易,但它没有 效果(不是我期望这种行为。关键是无法访问数据库上的事务并将其回滚。)
问题只是线程中止时没有正确清理事务。这是 .Net 1.1、2.0 和 2.0 SP1 的问题。我们正在运行 .Net 3.5 SP1。
这是一个说明问题的示例程序。
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 Hotfix targeted at .Net2.0 SP1 that was supposed to address this,但我们显然有较新的 DLL (.Net 3.5 SP1) 与此修补程序中列出的版本号不匹配。
谁能解释这种行为,为什么 ThreadAbort 仍然没有正确清理 sql 事务? .Net 3.5 SP1 是否不包含此修补程序,或者此行为在技术上是正确的?
【问题讨论】:
-
请不要对不使用 Thread.Abort 的 cmets - 我们没有在任何地方使用它。如果您不小心获得了应用程序域回收或其他原因,IIS 抛出的问题只是有时会导致它们。我们没有在代码中的任何地方使用 Thread.Abort :) 我们刚刚注意到这种行为并将其追溯到这种情况 - 示例程序显然是人为的。
-
如果您没有在代码中的任何地方使用 Thread.Abort,您可能希望将该注释 放入该代码,因为 Thread.Abort 非常显眼地放置在右侧在您在此处发布的代码中间。我知道这是示例代码之类的,但是您应该将注释放在那里,而不是放在注释中。否则你会得到那些cmets。
-
哈哈...我太慢了,无法先发制人。 SqlConnection 在 try/catch 之外的唯一原因是我可以在捕获 ThreadAbort 时尝试重新打开它。这个例子完全是人为的——它并不代表我们的真实代码。我们的交易并不完全是长期的。在非常重的负载下,有问题的查询的执行时间达到了大约 5 秒,这是我们开始注意到问题的时候。再次 - 人为的例子。我们能否请您关注我所询问的实际行为?
-
但是你已经成为另一个大禁忌的牺牲品,不要发布与生产代码有不同问题的代码的问题。人们会挂断你发布的代码,不管它是多么做作。他们会假设您已将问题缩小到此类代码,并正在寻求帮助以修复发布的代码。
-
@Lasse V. Karlsen 这不是一个不同的问题。它按照描述模拟问题(大概是为了让其他人可以对其进行测试,或者可以在单元测试中对其进行验证)。请注意 TSQL 中包含的
waitfor。
标签: c# .net sql-server multithreading thread-abort