好吧,这看起来像.NET中的一个主要基本错误:
考虑以下简单程序,它故意尝试连接到不存在的数据库:
class Program { static void Main(string[] args) { Thread threadOne = new Thread(GetConnectionOne); Thread threadTwo = new Thread(GetConnectionTwo); threadOne.Start(); threadTwo.Start(); } static void GetConnectionOne() { try { using (SqlConnection conn = new SqlConnection("Data Source=.\\wfea;Initial Catalog=zc;Persist Security Info=True;Trusted_Connection=yes;")) { conn.Open(); } } catch (Exception e) { File.AppendAllText("ConnectionOneError.txt", e.Message + "\n" + e.StackTrace + "\n"); } } static void GetConnectionTwo() { try { using (SqlConnection conn = new SqlConnection("Data Source=.\\wfea;Initial Catalog=zc;Persist Security Info=True;Trusted_Connection=yes;")) { conn.Open(); } } catch (Exception e) { File.AppendAllText("ConnectionTwoError.txt", e.Message + "\n" + e.StackTrace + "\n"); } } }
运行此程序并在catch块上设置断点.DBConnection对象将尝试连接15秒(在两个线程上),然后它将引发错误.检查异常的堆栈跟踪,堆栈跟踪将混合两个调用堆栈,如下所示:
at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject) at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject) at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject) at System.Data.SqlClient.SqlConnection.Open() at ZoCom2Test.Program.GetConnectionOne() in C:\src\trunk\ZTest\Program.cs:line 38 at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) at System.Data.SqlClient.SqlConnection.Open() at ZoCom2Test.Program.GetConnectionTwo() in C:\src\trunk\ZTest\Program.cs:line 54
您可能需要多次尝试才能实现此目的,但我现在正在我的机器上实现这一点.这怎么可能?这在VM级别应该是完全不可能的.看起来DBConnection.Open()函数同时在两个线程上抛出相同的异常,或类似的东西.
试试这个,看看会发生什么:
class ThreadingBug { private const string CONNECTION_STRING = "Data Source=.\\wfea;Initial Catalog=catalog;Persist Security Info=True;Trusted_Connection=yes;"; static void Main(string[] args) { try { Thread threadOne = new Thread(GetConnectionOne); Thread threadTwo = new Thread(GetConnectionTwo); threadOne.Start(); threadTwo.Start(); threadOne.Join(2000); threadTwo.Join(2000); } catch (Exception e) { File.AppendAllText("Main.txt", e.ToString()); } } static void GetConnectionOne() { try { using (SqlConnection conn = new SqlConnection(CONNECTION_STRING)) { conn.Open(); } } catch (Exception e) { File.AppendAllText("GetConnectionOne.txt", e.ToString()); } } static void GetConnectionTwo() { try { using (SqlConnection conn = new SqlConnection(CONNECTION_STRING)) { conn.Open(); } } catch (Exception e) { File.AppendAllText("GetConnectionTwo.txt", e.ToString()); } } }
我相信这里有一个错误,虽然它既不是主要的,也不是根本的.在努力缩小范围之后(以及执行诸如删除一个线程之类的操作)之后,看起来Exception
两个线程上的连接池实现抛出了类的相同实例(感谢Gregory发现这个).这有时会显示为损坏的("混合")堆栈跟踪,有时简单地表示两个线程上的堆栈跟踪相同,即使两个线程之间的代码完全不同.
注释掉其中一个Thread.Start
调用会显示完全不同的堆栈跟踪,这表明奇数部分位于连接池实现中 - 奇数堆栈跟踪由连接池分发,因为两个线程都使用相同的连接字符串和凭据.
我已经在https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=522506上提交了Connect问题.每个人都可以自由地投票决定你觉得它有多重要(或不重要),你是否可以重现它,或者你是否有一个解决方法.这将有助于Microsoft确定修复程序的优先级.
更新: Connect问题已更新.微软承认这是一个错误,并计划在未来的版本中修复它.
感谢nganju,Gregory和其他所有参与解决此问题的人.这确实是一个错误,它将被修复,这是因为我们.