2

我正在为我正在编写的多线程服务构建一个稍微更有效的连接池,以便在我的服务和它的 SQL 数据库之间进行通信。基本上归结为,我的课堂上有这段代码:

Public Class DBMC
Private Connections As New ArrayList

  Private Function GetConnection() As Object
    Dim conCounter As Integer = 0

    While True
        If Connections.Count > conCounter Then
            If System.Threading.Monitor.TryEnter(Connections(conCounter), 10) Then
                Return Connections(conCounter)
            Else
                conCounter += 1
            End If

        ElseIf conCounter > 98 Then
            'keep looping until an open connection is found
            conCounter = 0

        ElseIf conCounter < 98 Then
            Dim connection As Object = NewCon()

            If connection Is Nothing Then
                conCounter = 0
            Else
                Connections.Add(connection)

                Return connection
            End If
        End If
    End While
    'below line should never run
    Return Nothing
  End Function

  Public Function DBSelect(ByVal SQL As String) As DataSet
    Dim connection As Object = GetConnection()

    SyncLock (connection)
        'Run the select vs database and do a bunch of other stuff
    End SyncLock

    Try
        System.Threading.Monitor.Exit(connection)
    Catch ex As Exception
    End Try

    Return DataSet
  End Function
End Class

所以这段代码工作得非常好,我可以在不同的线程中运行 500 条 select 语句,就像计算机可以使它们一样快,它会打开 8 个不同的连接(可能是由于我的计算机的速度,较慢的 PC 可能会打开更多)。问题是,在 DBSelect 函数中,我在 Try/Catch 中有一行用于释放监视器 Enter 因为有时结束同步锁会丢弃我的对象上的锁(在这种情况下,不需要该行并引发异常),有时该对象仍处于锁定状态,并且将在不运行该行的情况下保持永久锁定(在这种情况下,它会使用它并成功通过)。我无法弄清楚为什么有时它会释放它,有时它不会。有任何想法吗?

4

1 回答 1

2

您收到异常的原因是,当您没有可用连接时,您创建一个新连接并返回它而不调用 Monitor.Enter() 。这意味着您将对该对象引用使用非递归 SyncLock,释放该锁,然后尝试在您从未使用过的附加锁上调用 Monitor.Exit()。

您在向池中添加连接的方式也有潜在的竞争条件。另一个线程很可能会锁定您刚刚创建的连接(通过 Monitor.TryEnter() 调用),然后再将其设置为 SyncLock 块以自行获取。如果您在将连接返回到池之前关闭连接(这是一个好主意),那么当您的创建线程实际使用它时,您的连接将处于错误状态。

我实际上建议您不要尝试编写自己的连接池。您当前的代码中没有任何内容表明您不能只使用 System.Data.SqlClient.SqlConnection 代替,它已经为您处理连接池。

于 2011-06-29T23:57:21.200 回答