.NET многопоточность Synclock против монитора - PullRequest
2 голосов
/ 29 июня 2011

Я создаю немного более эффективный пул соединений для многопоточного сервиса, который я пишу, чтобы общаться между моим сервисом и его базой данных 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 операторов выбора в разных потоках так быстро, как компьютер может их сделать, и он будетоткрыть 8 разных подключений (вероятно, из-за скорости моего компьютера, более медленный компьютер может открыть больше).Дело в том, что в функции DBSelect у меня есть строка, окруженная в Try / Catch, чтобы освободить монитор Enter, потому что иногда завершение синхронизации блокирует блокировки на моих объектах (в этом случае строка не нужна и выдает исключение), а иногдаобъект все еще заблокирован и будет постоянно заблокирован без выполнения этой строки (в этом случае он использует его и успешно проходит).Я не могу понять для себя всю жизнь, почему Иногда это выпускает, а иногда нет.Есть идеи?

1 Ответ

2 голосов
/ 30 июня 2011

Причина, по которой вы получаете исключение, заключается в том, что, когда у вас нет доступных подключений, вы создаете новое подключение и возвращаете его, не вызывая для него Monitor.Enter ().Это означает, что вы будете использовать нерекурсивный SyncLock для ссылки на этот объект, снимите эту блокировку и затем попытаетесь вызвать Monitor.Exit () для дополнительной блокировки, которую вы никогда не брали в первую очередь.

ВыТакже есть потенциальное состояние гонки, в зависимости от способа добавления соединений в пул.Другой поток вполне может взять блокировку только что созданного вами соединения (через вызов Monitor.TryEnter ()), прежде чем вы сделаете его блоком SyncLock, чтобы получить его самостоятельно.Если вы закрываете соединение перед его возвратом в пул (что является хорошей идеей), то когда ваш поток создания действительно начнет его использовать, у вас будет плохое состояние соединения.

Я бына самом деле предлагаю вам не пытаться написать свой собственный пул соединений.В вашем текущем коде нет ничего, что бы указывало на то, что вы не можете просто использовать System.Data.SqlClient.SqlConnection, который уже обрабатывает пул соединений для вас.

...