.NET 多线程同步时钟与监视器

发布于 2024-11-17 21:21:02 字数 1641 浏览 4 评论 0原文

我正在为一个多线程服务构建一个稍微更高效的连接池,我正在写信来在我的服务与其 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 个不同的连接(可能是由于我的计算机的速度,较慢的计算机可能会打开更多)。问题是,在 DBSelect 函数中,我有一行用 Try/Catch 包围来释放监视器 Enter,因为有时结束同步锁会释放对象上的锁(在这种情况下,不需要该行并引发异常),有时该对象仍然被锁定,并且将保持永久锁定而不运行该行(在这种情况下,它使用它并成功通过)。我一生都无法弄清楚为什么有时它会释放它,有时却不会。有什么想法吗?

I'm building a slightly more efficient connection pool for a multi threaded service I'm writing to talk between my service and its SQL database. basically boiled down, I've got this code in my class:

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

So this code works absolutely great, I can run 500 select statements in different threads as fast as the computer can make them and it will open 8 different connections (likely due to the speed of my computer, a slower PC may open more). The thing is, in the DBSelect function, I have a line surrounded in Try/Catch to release the monitor Enter because sometimes ending the synclock drops the locks on my objects (in this case the line is not needed and throws an exception) and sometimes the object is still locked and would stay permanently locked without running that line (in which case it uses it and passes successfully). I cannot figure out for the life of me why Sometimes it releases it, and sometimes it doesn't. Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

心病无药医 2024-11-24 21:21:02

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

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

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

The reason you're getting an exception is that, when you have no available connections, you create a new connection and return it without calling Monitor.Enter() on it. This means that you'll take a non-recursive SyncLock to that object reference, release that lock and then try to call Monitor.Exit() on an additional lock that you never took in the first place.

You also have a potential race condition surrounding the way you're adding connections to the pool. Another thread could very well take a lock to the connection that you just created (through the Monitor.TryEnter() call) before you ever make it the SyncLock block to take it yourself. If you're closing the connection before returning it to the pool (which is a good idea) then when your creating thread actually gets to use it then you'll have a connection in a bad state.

I would actually suggest that you don't try to write your own connection pool. Your current code has nothing in it that suggests that you couldn't just use System.Data.SqlClient.SqlConnection instead, which already handles connection pooling for you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文