我正在编写一个查看SQL的VB.net 2017 Windows服务,根据行数,它将创建多个线程。这些线程监视文件夹并报告回不同的表并相应地记录数据。这段代码已经运行了几年并且一直运行良好但是在最近几天,我决定将它从启动时运行的控制台应用程序切换到窗口服务,这是我第一次写一个Windows服务。
我已经完成了代码工作,但测试是一个很大的痛苦,因为我无法浏览代码。我做了一些更改并合并了一些重复的部分。例如,我编写了4或5个不同的代码段来将数据写入SQL或从SQL中提取数据。我将它们整合到只有2个子例程,并且线程继续使用它们。根据具体情况,程序可以有1到15个线程,当我开始激活更多线程时,我开始遇到问题。在我移植它之前,我已经在控制台应用程序的代码中尝试了语句,我只是在创建新程序时将这些语句放入日志表中,而且它抱怨我正在尝试打开“打开连接”。下面是一个从SQL中提取数据的例程的示例:
Public con As New SqlClient.SqlConnection
Public dsGeneral As New DataSet
Public dc1 As SqlClient.SqlCommand
Public pullGeneral As SqlClient.SqlDataAdapter
Public maxRowsGeneral As Integer
Public Sub PullGeneralSQL(ByVal SQL As String)
Try
If (con.State = ConnectionState.Closed) Then
con.Open()
End If
dsGeneral.Tables.Clear()
pullGeneral = New SqlClient.SqlDataAdapter(SQL, con)
pullGeneral.Fill(dsGeneral, "General")
maxRowsGeneral = dsGeneral.Tables("General").Rows.Count
Catch ex As Exception
Msg(ex.Message, "Error")
maxRowsGeneral = 0
End Try
con.Close()
End Sub
我也遇到错误,说连接已经关闭。我假设另一个线程已经完成连接并且在线程处于任务中间时关闭了连接。
我的问题是,处理此问题的最佳方法是什么?
答案 0 :(得分:0)
我之前遇到过这个问题,即使最终有连接池,当你打开和关闭以及打开和关闭连接时,数据库最终会抛出错误。在我的情况下,一遍又一遍是600线程。工作了一会儿然后扔了相同类型的错误。
我的解决方案是创建自己的连接池。
Public Class Form1
Public dsGeneral As New DataSet
Public dc1 As SqlClient.SqlCommand
Public pullGeneral As SqlClient.SqlDataAdapter
Public maxRowsGeneral As Integer
Public Sub PullGeneralSQL(ByVal SQL As String)
'Get a connection from the list..Thread safe
'Your thread will wait here until there is a connection to grab.
'Since your max is 15 the 16++ threads will all wait their turn
Dim con As SqlClient.SqlConnection = ConnectionManager.WaitForConnection()
Try
dsGeneral.Tables.Clear()
pullGeneral = New SqlClient.SqlDataAdapter(SQL, con)
pullGeneral.Fill(dsGeneral, "General")
maxRowsGeneral = dsGeneral.Tables("General").Rows.Count
Catch ex As Exception
Msg(ex.Message, "Error")
maxRowsGeneral = 0
End Try
'Put the connection back into the list
'Allows another thread to start
ConnectionManager.ReleaseConnection(con)
End Sub
End Class
Public Class ConnectionManager
Public Shared poolLimit As Integer = 15 'Maximum number of connections to open.
Public Shared connectionString As String = "PUT YOUR CONNECTION STRING HERE"
'Since this is static it will create 15 connections and add them to the list when the service starts
Private Shared ConnectionList As New IThreadPool(Of SqlClient.SqlConnection)(Function()
Dim connection As New SqlClient.SqlConnection(connectionString)
connection.Open()
Return connection
End Function, poolLimit)
''' <summary>
''' Gets the pool count.
''' Purely for information to get the number of connections left in the list
''' </summary>
Public ReadOnly Property PoolCount() As Integer
Get
Return ConnectionList.PoolCount
End Get
End Property
''' <summary>
''' Waits until there is a free connection in the list
''' When there is a free connection grab it and hold it
''' </summary>
Public Shared Function WaitForConnection() As SqlClient.SqlConnection
Try
Return ConnectionList.GetObject()
Catch ex As Exception
'only during close
Throw ex
End Try
Return Nothing
End Function
''' <summary>
''' Releases the connection.
''' Put the connection back into the list.
''' </summary>
Public Shared Sub ReleaseConnection(connection As SqlClient.SqlConnection)
Try
ConnectionList.PutObject(connection)
Catch ex As Exception
'only during close
Throw ex
End Try
End Sub
''' <summary>
''' Disposes this instance.
''' Make sure to dispose when the service shuts down or your connections will stay active.
''' </summary>
Public Shared Sub Dispose()
ConnectionList.Dispose()
End Sub
End Class
Public Class IThreadPool(Of T)
Private connections As System.Collections.Concurrent.BlockingCollection(Of T)
Private objectGenerator As Func(Of T)
Public Sub New(objectGenerator As Func(Of T), boundedCapacity As Integer)
If objectGenerator Is Nothing Then
Throw New ArgumentNullException("objectGenerator")
End If
connections = New System.Collections.Concurrent.BlockingCollection(Of T)(New System.Collections.Concurrent.ConcurrentBag(Of T)(), boundedCapacity)
Me.objectGenerator = objectGenerator
Task.Factory.StartNew(Function()
If connections.Count < boundedCapacity Then
Parallel.[For](0, boundedCapacity, Function(i)
Try
If connections.Count < boundedCapacity Then
connections.Add(Me.objectGenerator())
End If
Catch ex As Exception
'only error during close
End Try
End Function)
Try
While connections.Count < boundedCapacity
connections.Add(Me.objectGenerator())
End While
Catch ex As Exception
'only error during close
End Try
End If
End Function)
End Sub
Public ReadOnly Property PoolCount() As Integer
Get
Return If(connections IsNot Nothing, connections.Count, 0)
End Get
End Property
Public Function GetObject() As T
Return connections.Take()
End Function
Public Sub PutObject(item As T)
connections.Add(item)
End Sub
Public Sub Dispose()
connections.Dispose()
End Sub
End Class