VB.NET多线程操作MySQL报错:连接关联已打开的DataReader
问题描述
调用MySQL数据获取方法时触发以下错误:
There is already an open DataReader associated with this Connection which must be closed first
我的实现代码如下:
1. 模块中的连接检查函数
Public Function CheckServerConn() As Boolean Try _ServerConnStr = New MySqlConnection(My.Settings.DbPath) If _ServerConnStr.State = ConnectionState.Open Then _ServerConnStr.Close() End If If _ServerConnStr.State = ConnectionState.Closed Then _ServerConnStr.Open() Return True End If Catch ex As Exception MsgBox("Check The Conn " & ex.Message, Me_MsgInfoStyle, Me_MsgCaptionStr) Return False End Try #Disable Warning BC42353 ' Function doesn't return a value on all code paths End Function
2. ClsMySql类中的GetData子过程
'GetData Public Sub GetData(ByVal SqlStr As String, ByVal xDt As DataTable, ByVal xPar() As MySqlParameter) Using xCmd As New MySqlCommand() With { .CommandType = CommandType.Text, .CommandText = SqlStr, .Connection = _ServerConnStr, .CommandTimeout = 5000000 } If xPar IsNot Nothing Then For i As Integer = 0 To xPar.Length - 1 xCmd.Parameters.Add(xPar(i)) Next End If Using xDa As New MySqlDataAdapter(xCmd) xDa.Fill(xDt) End Using xDt.Dispose() End Using End Sub
3. 数据表类中的数据获取方法
Public Sub MySql_Get_Daf() xClsMySql = New ClsMySql Dim SqlStr As String = "SELECT RegID,RegType, tblregs1.`RegRef`,`RegDate`, COUNT(`RegdID`) AS xCount ,IF(COUNT(`RegdID`) =3,'Ok','Error') AS xResult FROM tblregs1 INNER JOIN tblregs2 ON tblregs2.RegRef = tblregs1.RegRef WHERE `RegType` = 'Daf' GROUP BY tblregs1.`RegRef` ORDER BY COUNT(`RegdID`) ASC" Dt_Get_Daf = New DataTable() xClsMySql.GetData(SqlStr, Dt_Get_Daf, Nothing) End Sub Public Sub MySql_Get_Qbd() xClsMySql = New ClsMySql Dim SqlStr As String = "SELECT RegID,RegType, tblregs1.`RegRef`,`RegDate`, COUNT(`RegdID`) AS xCount ,IF(COUNT(`RegdID`) =3,'Ok','Error') AS xResult FROM tblregs1 INNER JOIN tblregs2 ON tblregs2.RegRef = tblregs1.RegRef WHERE `RegType` = 'Qbd' GROUP BY tblregs1.`RegRef` ORDER BY COUNT(`RegdID`) ASC" Dt_Get_Qbd = New DataTable() xClsMySql.GetData(SqlStr, Dt_Get_Qbd, Nothing) End Sub Public Sub MySql_Get_All() Dim xThread As Thread = New Thread(Sub() MySql_Get_Daf()) Dim xThread2 As Thread = New Thread(Sub() MySql_Get_Qbd()) xThread.Start() xThread2.Start() End Sub
当通过按钮调用MySql_Get_All方法时触发上述错误,请问问题出在哪里?
问题原因与解决方法
核心问题
你在多线程环境下共享了同一个数据库连接对象_ServerConnStr。MySqlConnection本身不是线程安全的,当两个线程同时通过这个连接执行查询时,第一个线程的DataReader还在占用连接,第二个线程又尝试用同一个连接发起新操作,直接触发了连接被占用的错误。
另外代码还有两个额外问题:
CheckServerConn每次调用都会创建新连接并打开,后续所有操作复用这个全局连接,既违背了连接池的最佳实践,也必然在多线程下冲突。GetData中调用了xDt.Dispose(),但xDt是外部传入的DataTable,你不应在方法内释放外部资源,会导致调用方后续无法使用该DataTable。
解决步骤
1. 移除全局连接,每个操作单独创建并管理连接
修改GetData方法,内部创建连接,不再依赖全局_ServerConnStr:
' 修改后的GetData方法 Public Sub GetData(ByVal SqlStr As String, ByVal xDt As DataTable, ByVal xPar() As MySqlParameter) ' 每个操作单独创建连接,Using会自动释放连接回连接池 Using conn As New MySqlConnection(My.Settings.DbPath) Using xCmd As New MySqlCommand() With { .CommandType = CommandType.Text, .CommandText = SqlStr, .Connection = conn, .CommandTimeout = 5000000 } If xPar IsNot Nothing Then xCmd.Parameters.AddRange(xPar) ' 用AddRange替代循环更简洁 End If conn.Open() Using xDa As New MySqlDataAdapter(xCmd) xDa.Fill(xDt) End Using End Using End Using ' 移除xDt.Dispose(),让调用方自己管理DataTable生命周期 End Sub
2. 废弃原有的CheckServerConn函数
现在每个操作都会自行创建并打开连接,不需要提前维护全局连接。连接池会自动管理连接复用,比手动维护高效且安全。
3. 优化多线程调用(可选)
可以用Task替代Thread,更符合.NET异步编程规范:
Public Async Sub MySql_Get_All() Await Task.WhenAll( Task.Run(AddressOf MySql_Get_Daf), Task.Run(AddressOf MySql_Get_Qbd) ) End Sub
关键原理
MySQL连接对象(多数ADO.NET连接对象)都是线程不安全的,绝对不能在多线程间共享。正确做法是每次数据库操作创建新连接实例,通过Using自动释放,连接池会在后台高效复用物理连接,不会造成性能损耗。
内容的提问来源于stack exchange,提问作者M.J
相关产品推荐
相关产品推荐

