.NET多线程Synclock与Monitor



我正在为一个多线程服务构建一个稍微高效的连接池,我正在编写这个连接池,以便在我的服务和它的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,因为有时结束同步锁会丢失对象上的锁(在这种情况下,不需要该行,并引发异常),有时对象仍然被锁定,并且在不运行该行的情况下会永久锁定(在这种情形下,它使用该行并成功通过)。我一辈子都不明白为什么有时它会释放它,有时它不会。有什么想法吗?

出现异常的原因是,当您没有可用的连接时,您会创建一个新连接并返回它,而无需对其调用Monitor.Enter()。这意味着您将对该对象引用使用非递归SyncLock,释放该锁,然后尝试对一个最初从未使用过的附加锁调用Monitor[Enter(。

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

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

最新更新