Brian,
| But, for those who may want a
| solution that offers complete thread-safety (multiple adders and
| getters)
The hazards of cutting & pasting a post I made a year or so ago...
| Public Function GetRequest() As Object
| Do While True
| m_event.WaitOne()
| SyncLock m_padlock
| If m_queue.Count > 0 Then
| Return m_queue.Dequeue()
| End If
| End SyncLock
| Loop
| End Function
Your loop is superfluous! I would reduce GetRequest to:
| Public Function GetRequest() As Object
| m_event.WaitOne()
| SyncLock m_padlock
| If m_queue.Count > 0 Then
| Return m_queue.Dequeue()
| End If
| End SyncLock
| End Function
Your loop is superfluous, as the m_event.WaitOne will only allow a single
thread to pass (as its an AutoResetEvent). The event being set indicates
that at least a single item is in the queue. The return statement will exit
the loop. Ergo the loop itself is superfluous. Of course! this also means
that the "If m_queue.Count > 0 Then" is superfluous , so we can reduce it
further to:
| Public Function GetRequest() As Object
| m_event.WaitOne()
| SyncLock m_padlock
| Return m_queue.Dequeue()
| End SyncLock
| End Function
The "If m_queue.Count > 0 Then" is superfluous again, as the m_event.WaitOne
will only allow a single thread to pass (as its an AutoResetEvent)...
However! (loop or no loop, if or no if) you just introduced a severe bug I
was trying to avoid, all the threads block waiting for the next event,
although there are multiple entries in the queue.
Consider the case where one or more threads add 2 or more items to the Queue
before any of the readers have a chance to remove the next item. There is
only a single AutoResetEvent event, it is either set or reset. 2 items go
in, its set, one item comes out, its reset. All the threads block for the
third item, the third item goes in, the second item comes out, all the
thread block for the four item, items 4 to 10 goes in, item 3 comes out...
The way I setup GetRequest was:
- If there are items in the queue, return the next item, only allow a single
thread to do this! (prevent the above mentioned blockage).
- If there are no items in the queue, wait for the event, be certain to
allow threads to add to the queue
- The event was set, must be an item, return the next item. *** red flag ***
Ah! There's the rub, the first Dequeue might return the item before the
second Dequeue has a chance to see it:
For now I will use a variation of both of ours:
| > ' called from the Worker thread
| > Public Function GetRequest() As Object
| > ' Check to see if there are already items available
| > SyncLock m_padlock
| > If m_queue.Count() > 0 Then
| > Return m_queue.DeQueue()
| > End If
| > End SyncLock
| >
| Do While True
| m_event.WaitOne()
| SyncLock m_padlock
| If m_queue.Count > 0 Then
| Return m_queue.Dequeue()
| End If
| End SyncLock
| Loop
| > End Function
As this allows for the case where reader 1 gets the first Dequeue, while
reader 2 gets the WaitOne there by missing the second Dequeue, that the
first Dequeue just got... I wasn't considering that scenario as the original
code was not intended for multiple readers... Thanks! for pointing it out.
--
Hope this helps
Jay [MVP - Outlook]
..NET Application Architect, Enthusiast, & Evangelist
T.S. Bradley -
http://www.tsbradley.net
"Brian Gideon" <br*********@yahoo.com> wrote in message
news:11**********************@g14g2000cwa.googlegr oups.com...
| There may be a subtle race condition in the ThreadRequestQueue class.
| It depends on the level of thread-safety offered. As written it is
| perfectly safe for one adder and one getter simultaneously. And that's
| fine if that is it's intended use case. But, for those who may want a
| solution that offers complete thread-safety (multiple adders and
| getters) I recommend the following implementation.
|
| Public Class ThreadRequestQueue
|
| Private Readonly m_padlock As New Object
| Private Readonly m_queue As New Queue
| Private Readonly m_event As New AutoResetEvent(False)
|
| ' Can be called from any thread
| Public Sub AddRequest(ByVal request As Object)
| SyncLock m_padlock
| m_queue.Enqueue(Object)
| m_event.Set()
| End SyncLock
| End Sub
|
| ' Can be called from any thread
| Public Function GetRequest() As Object
| Do While True
| m_event.WaitOne()
| SyncLock m_padlock
| If m_queue.Count > 0 Then
| Return m_queue.Dequeue()
| End If
| End SyncLock
| Loop
| End Function
|
| End Class
|
| Brian
|
| Jay B. Harlow [MVP - Outlook] wrote:
| > | Suppose the worker thread displays a form and the thread that created
it
| > | wants to change that form's text occasionally.
| > If the worker thread is showing a Form, then you can use Control.Invoke
on
| > that form or one of its control to transfer information to the worker
| > thread.
| >
| > Alternatively if the worker thread does not have a Form, I have used a
| > System.Collections.Queue to send information to a thread. The thread
would
| > look for information in the queue & operate on it.
| >
| > Something like (VS 2003 syntax):
| >
| > ' untested, typed from memory.
| > Public Class ThreadRequestQueue
| >
| > Private Readonly m_padlock As New Object
| > Private Readonly m_queue As New Queue
| > Private Readonly m_event As New AutoResetEvent(False)
| >
| > ' called from the Main thread
| > Public Sub AddRequest(ByVal request As Object)
| > SyncLock m_padlock
| > m_queue.Enqueue(Object)
| > End SyncLock
| > m_event.Set()
| > End Sub
| >
| > ' called from the Worker thread
| > Public Function GetRequest() As Object
| > ' Check to see if there are already items available
| > SyncLock m_padlock
| > If m_queue.Count() > 0 Then
| > Return m_queue.DeQueue()
| > End If
| > End SyncLock
| >
| > ' Cannot block worker thread
| > ' while waiting for the main thread to add requests
| > m_event.WaitOne()
| >
| > ' There must be an item
| > SyncLock m_padlock
| > Return m_queue.Dequeue()
| > End SyncLock
| > End Function
| >
| > End Class
| >
| > The Queue is used to send the requests from the Main thread to the
Worker
| > thread. The m_padlock is used to protect the Queue.Enqueue &
Queue.Dequeue
| > methods. The worker thread "goes to sleep" if there are no items in the
| > queue to work on, the AutoResetEvent is used to notify (wake up) the
worker
| > thread there are more items available.
| >
| > In VS 2005 (.NET 2.0) I would consider using a
| > System.Collections.Generic.Queue(Of T) instead of the object based Queue
| > above:
| >
http://msdn2.microsoft.com/en-us/library/7977ey2c.aspx
| >
| > --
| > Hope this helps
| > Jay [MVP - Outlook]
| > .NET Application Architect, Enthusiast, & Evangelist
| > T.S. Bradley -
http://www.tsbradley.net
| >
| >
| > " **Developer**" <RE*************@a-znet.com> wrote in message
| > news:Oe*************@TK2MSFTNGP11.phx.gbl...
| > |I have a simple need that I can't seem to locate the answer to in the
docs.
| > | Most examples show how a worker thread can pass data back to the
thread
| > that
| > | created it.
| > |
| > | I need to do the opposite.
| > | Suppose the worker thread displays a form and the thread that created
it
| > | wants to change that form's text occasionally.
| > |
| > |
| > |
| > | Can't seem to locate a simple way of doing that.
| > |
| > |
| > |
| > | Thanks in advance
| > |
| > |
| > |
|