"Iason Mavip" <op*****@catholic.orgschrieb
I have got two classes: One producing data and one consuming data.
Running as two threads, the first class adds data to a job-queue,
the second one fetches data from the queue.
My problem is that the consumer thread spends most of its time
waiting for data to appear on the queue. How can I tell the consumer
that data is available, without running useless circles in an
do...loop?
Thanks in advance!
Iason
Code example below:
Class Jobs
Private o As New Generic.Queue(Of Integer)
Private b As Boolean
Public Property Done() As Boolean
Get
Return o.Count = 0 And b
End Get
Set(ByVal value As Boolean)
b = value
End Set
End Property
Public Sub Add(ByVal n As Integer)
o.Enqueue(n)
End Sub
Public Function Fetch() As Integer
If o.Count 0 Then
Return o.Dequeue
Else
Return Nothing
End If
End Function
End Class
Class Producer
Public Sub Produce(ByVal o As Object)
Dim q As Jobs = CType(o, Jobs)
For i As Integer = 1 To 100
q.Add(i)
Threading.Thread.Sleep(100)
Next
o.Done = True
End Sub
End Class
Class Consumer
Public Sub Comsume(ByVal o As Object)
Dim q As Jobs = CType(o, Jobs)
Do While Not q.Done
Debug.Print(q.Fetch)
Loop
End Sub
End Class
First, it's strongly recommended to switch Option Strict On.
Untested(!) solution:
Class Jobs
Public Event ItemAdded()
Public Event DoneChanged()
Private o As New Generic.Queue(Of Integer)
Private b As Boolean
Public Property Done() As Boolean
Get
Return o.Count = 0 And b
End Get
Set(ByVal value As Boolean)
b = value
RaiseEvent DoneChanged()
End Set
End Property
Public Sub Add(ByVal n As Integer)
o.Enqueue(n)
RaiseEvent ItemAdded()
End Sub
Public Function Fetch() As Integer
If o.Count 0 Then
Return o.Dequeue
Else
Return 0
End If
End Function
End Class
Class Producer
Public Sub Produce(ByVal q As Jobs)
For i As Integer = 1 To 100
SyncLock q
q.Add(i)
End SyncLock
Threading.Thread.Sleep(100)
Next
q.Done = True
End Sub
End Class
Class Consumer
Private ARE As New Threading.AutoResetEvent(False)
Public Sub Comsume(ByVal q As Jobs)
AddHandler q.ItemAdded, AddressOf OnItemAdded
AddHandler q.DoneChanged, AddressOf OnDoneChanged
Do
ARE.WaitOne()
Do Until q.Done
Dim value As Integer
SyncLock q
value = q.Fetch
End SyncLock
Debug.Print(value.ToString)
If value = 0 Then Exit Do
Loop
Loop Until q.Done
End Sub
Private Sub OnItemAdded()
ARE.Set()
End Sub
Private Sub OnDoneChanged()
ARE.Set()
End Sub
End Class
The AutoResetEvent is the key. It waits til the queue is "done" or an item
has been added w/o CPU usage.
Are you sure that the Queue will never contain 0? Otherwise, returning
0 for an empty queue is ambiguous. I would add a Count property, or
you can Inherit from the generic Queue.
(BTW, I'd prefer stopping the Consumer processing the queue instead of
setting a Done flag in a queue, but maybe there's a reason for you doing it
this way. In addition, the Done property could be set from True to False
which wouldn't make sense.)
Armin