从线程

时间:2017-06-23 07:34:51

标签: vb.net multithreading

我正在编写一个查看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

我也遇到错误,说连接已经关闭。我假设另一个线程已经完成连接并且在线程处于任务中间时关闭了连接。

我的问题是,处理此问题的最佳方法是什么?

1 个答案:

答案 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