Безопасность потоков в VB.Net - PullRequest
4 голосов
/ 21 июля 2011

Я пытаюсь запустить многопоточное консольное приложение в VB, и у меня пересекаются потоки.По сути, я хочу запустить 5 потоков, чтобы они постоянно обращались к очереди, обрабатывали и повторяли, пока ничего не осталось.Когда все потоки обработаны, я хочу, чтобы они занялись чем-то другим.Я пытаюсь использовать SyncLock для предотвращения доступа нескольких потоков, но, похоже, он не работает.Любая помощь будет оценена!

Dim iThread As Integer
Dim manualEvents(4) As ManualResetEvent

Sub Main()
    For i = 0 To 4
        manualEvents(i) = New ManualResetEvent(False)
        ThreadPool.QueueUserWorkItem(AddressOf DoOne)
    Next

    For Each handle As WaitHandle In manualEvents
        handle.WaitOne()
    Next

    ' do other stuff
EndSub

Private Sub DoOne()
    Dim lockObject As New Object()
    SyncLock (lockObject)
        If QueryQueue.DoOne() Then
            DoOne()
        Else
            manualEvents(iThread).Set()
            iThread = iThread + 1
        End If
    End SyncLock
End Sub

Ответы [ 3 ]

4 голосов
/ 21 июля 2011

Проблема с заблокированным ресурсом, вы используете lockObject в качестве ресурса блокировки синхронизации, который должен использоваться совместно с потоками.Вы должны сделать это полем экземпляра.

Private Shared lockObject As New Object()
Private Sub DoOne()
  SyncLock (lockObject)
    If QueryQueue.DoOne() Then
        DoOne()
    Else
        manualEvents(iThread).Set()
        iThread = iThread + 1
    End If
  End SyncLock
End Sub
4 голосов
/ 21 июля 2011

Проблема в том, что вы создаете и используете новый экземпляр объекта для блокировки в каждом потоке. Наивным решением является продвижение lockObject из локальной переменной в переменную класса. Таким образом, каждый поток использует один и тот же объект для блокировки. Я говорю, что это наивно, потому что вы обменяли одну проблему на другую (хотя и менее серьезную). Новая проблема заключается в том, что теперь вы сделали свой параллельный алгоритм последовательным алгоритмом, поскольку в любой момент времени только один поток может выполнять работу.

Решением будет блокировка доступа к очереди только в то время, когда она изменяется. Затем работайте с объектами, снятыми с очереди, вне блокировки, чтобы потоки могли выполнять работу одновременно.

Если доступен .NET 4.0, вы можете изменить код следующим образом.

Public Class Example

  Private m_Queue As ConcurrentQueue(Of SomeObject) = New ConcurrentQueue(Of SomeObject)()

  Public Shared Sub Main()

    ' Populate the queue here.

    Dim finished = New CountdownEvent(1)
    For i As Integer = 0 to 4
      finsihed.AddCount()
      ThreadPool.QueueUserWorkItem(AddressOf DoOne, finished)
    Next
    finished.Signal()
    finished.Wait()

  End Sub

  Private Shared Sub DoOne(ByVal state As Object)
    Try
      Dim item As SomeObject = Nothing
      Do While m_Queue.TryDequeue(item) Then
        ' Process the dequeued item here.
      Loop
      ' There is nothing left so do something else now.
    Finally
      Dim finished = DirectCast(state, CountdownEvent)
      finished.Signal()
    End Try
  End Sub

End Class

Я использовал ConcurrentQueue, чтобы избежать необходимости использовать SyncLock полностью. Я использовал CountdownEvent в качестве более масштабируемой альтернативы для ожидания выполнения рабочих элементов.

2 голосов
/ 21 июля 2011

Вам необходимо совместно использовать один и тот же объект lockObject для всех потоков, сделав его переменной экземпляра.

...