我做的一个Windows Form 程序碰到一个很怪异的多线程情况,最后检查进去竟然是部分代码的数据库链接没有关闭导致的。
我的这个程序是多线程程序,每个线程不间断的从数据库中取得数据,然后对取出的数据进行处理,一直循环到没有需要处理的数据为至。每个线程的循环是上万次的,即,每个线程上万次的数据库链接打开操作。
这个程序碰到怪异的现象是:
在A服务器上,没有任何问题,在B服务器上程序开一个线程没有任何问题,开多个线程则只有一个线程没问题,其他线程都有问题。
对每一个线程都作 try catch 拦截,就会看到出错线程报如下错误:
超时时间已到。超时时间已到,但是尚未从池中获取连接。出现这种情况可能是因为所有池连接均在使用,并且达到了最大池大小。
System.Data
在 System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
在 System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
在 System.Data.SqlClient.SqlConnection.Open()
从这个异常就可以看到,是数据库链接对象池中没有可用的链接了。
再分析下去,就是其中一部分从数据库中读数据的代码,没有调用 SqlConnection.Close。
分析我这个程序出现的规律:
单线程,不主动调用 SqlConnection.Close
这时候竟然数据库链接池不会报没有可用资源:
数据库链接对象池的默认最大容量为100,单线程对数据库链接打开的请求超过10000的。
显然这种情况下,虽然没有手工SqlConnection.Close,但是这些数据库链接SqlConnection还是回到了数据库链接对象池中了。
多线程时,不主动调用 SqlConnection.Close。
只有一个线程在跑,其他线程都报上述错误。
显然,多线程时,数据库链接对象池都被一个线程占住了,其他线程获得不了可用分数据库链接。
这个多线程怪异的特征,之前一直没想到会是SqlConnection.Close导致的,一直在其他方面想可能的问题。这次经验教训后,心得就是:
- try catch 只能拦截当前线程的异常,它拦截不到其他线程的。
- 多人协作开发,并且程序经过多次修改bug后,完全有可能出现某些情况下,没有关闭数据库链接的情况,这种情况如何避免,是必须深思的一个问题。
- 单线程,不主动关闭SqlConnection,有可能仍然不报错误,但是多线程下,就可能报错误了。
参考资料:
FIX: Error message when a "System.Data" thread tries to open a pooled connection in the .NET Framework 2.0: "Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool
http://support.microsoft.com/kb/948868
http://support.microsoft.com/kb/948868/en-us
使用连接池
http://msdn.microsoft.com/zh-cn/library/8xx3tyca(VS.80).aspx