Home All Groups Group Topic Archive Search About

Thread Sync Queue Problem

Author
8 May 2006 3:53 PM
shipcreak
I have an interesting problem with a sort of producer-consumer system
for error logging.  Consider the following code:

<code>
SyncLock _eventList.SyncRoot

    Dim item As ExceptionLogEntry

    ' If there are items, get the top one
    If _eventList.Count > 0 Then

        ' Get the item from the queue
        item = CType(_eventList.Dequeue, _
            ExceptionLogEntry)

        ' If we got an item to log, log it
        If Not item Is Nothing Then

            WriteToLog(BuildExceptionReport(item))

        End If

    End If

End SyncLock
</code>

This method is running as a new thread, created thus:

<code>
_thread = New Thread(AddressOf StartWatching)
_thread.Start()
</code>

FACTS:
1)  The _thread is System.Threading.Thread, the _eventList is
System.Collections.Queue, both are member variables of the class.

2)  The class this code appears in could have more than one instances
(hence using threading).

3)  I'm using .NET 1.1.

4)  The _eventList object is referenced by the producer class (not
listed here), where Enqueue is called inside a SyncLock block.

MY PROBLEM:
Thing is, it seems that "_eventList.count > 0" returns true, and still
is just before the dequeue call, but after the dequeue call it goes to
0, and "item" is NOTHING!!

I have no idea why this could be.  Can anyone help!?!

Cheers,

<Shipcreak />



Full class follows:

Imports System.Text
Imports System.Threading

Public MustInherit Class LoggerBase

    Private _name As String
    Private _eventList As Queue
    Private _thread As Thread
    Private _stopRequested As Boolean

    Public Sub New(ByVal pName As String)
        _name = pName
    End Sub

    Public Sub AssignEventList(ByVal eventList As Queue)
        If Not _eventList Is Nothing Then
            SyncLock _eventList
                _eventList = eventList
            End SyncLock
        Else
            _eventList = eventList
        End If
    End Sub

    Public Sub Start()

        _stopRequested = False

        _thread = New Thread(AddressOf StartWatching)

        If Not _name Is Nothing AndAlso _name.Length > 0 Then
            _thread.Name = _name
        Else
            _thread.Name = "Event Logging Thread"
        End If

        _thread.Start()

    End Sub

    Public Sub RequestStop()
        _stopRequested = True
    End Sub

    Private Sub StartWatching()

        While Not _stopRequested

            ' Lock the list
            SyncLock _eventList.SyncRoot

                Dim item As ExceptionLogEntry

                ' If there are items, get the top one
                If _eventList.Count > 0 Then

                    ' Get the item from the queue
                    item = CType(_eventList.Dequeue, ExceptionLogEntry)

                    ' If we got an item to log, log it
                    If Not item Is Nothing Then

                        WriteToLog(BuildExceptionReport(item))

                    End If

                End If

            End SyncLock


            ' Wait for 1 sec to allow other threads to take their turn
            Thread.CurrentThread.Sleep(100)

        End While

    End Sub

    Private Function BuildExceptionReport( _
            ByVal ex As ExceptionLogEntry) As String

        Dim ret As New StringBuilder
        Dim e As ExceptionLogEntry = ex

        If Not e Is Nothing Then

            ret.AppendFormat("OS: {0}" & vbCrLf, ex.OSInfo)
            ret.AppendFormat("Processor usage: {0}" & vbCrLf, _
                            ex.ProcessorUsage)
            ret.AppendFormat("Working Set: {0}" & vbCrLf, _
                            ex.WorkingSet)
            ret.AppendFormat("Free Memory: {0}" & vbCrLf, _
                            ex.FreeMemory)
            ret.AppendFormat("OS UserName: {0}" & vbCrLf, _
                            ex.OSUserName)
            ret.AppendFormat("Machine Name: {0}" & vbCrLf, _
                            ex.MachineName)
            'ret.AppendFormat("Total HDD: {0}" & vbCrLf, _
                            ex.HDDTotal)
            'ret.AppendFormat("Free HDD: {0}" & vbCrLf, _
                            ex.FreeHDD)
            ret.AppendFormat("Occurred At: {0}" & vbCrLf, _
                            ex.Occurred)
            ret.Append("Exception details:" & vbCrLf)

            AppendExceptionDetails(ex.CausingException, ret)

            WriteToLog(ret.ToString)

        Else

            WriteToLog("No exception data provided")

        End If

    End Function

    Private Sub AppendExceptionDetails(ByVal ex As Exception, _
                                    ByVal report As StringBuilder)

        Dim e As Exception = e

        Do While Not e Is Nothing

            report.AppendFormat("  Message: {0}" & vbCrLf, _
                                e.Message)
            report.AppendFormat("  Source: {0}" & vbCrLf, _
                                e.Source)
            report.AppendFormat("  Method: {0}" & vbCrLf, _
                                e.TargetSite.Name)
            report.AppendFormat("  Help Link: {0}" & vbCrLf, _
                                e.HelpLink)
            report.AppendFormat("  Stack Trace: {0}" & vbCrLf, _
                                e.StackTrace)
            report.Append("------------------" & vbCrLf)

            e = e.InnerException

        Loop

    End Sub

    Protected MustOverride Sub WriteToLog(ByVal item As String)
    Protected MustOverride Sub WriteToLog(ByVal item As String, _
                                ByVal type As EventLogEntryType)

End Class

Author
8 May 2006 4:02 PM
Larry Lard
shipcr***@gmail.com wrote:
Show quoteHide quote
> I have an interesting problem with a sort of producer-consumer system
> for error logging.  Consider the following code:
>
> <code>
> SyncLock _eventList.SyncRoot
>
>     Dim item As ExceptionLogEntry
>
>     ' If there are items, get the top one
>     If _eventList.Count > 0 Then
>
>         ' Get the item from the queue
>         item = CType(_eventList.Dequeue, _
>             ExceptionLogEntry)
>
>         ' If we got an item to log, log it
>         If Not item Is Nothing Then
>
>             WriteToLog(BuildExceptionReport(item))
>
>         End If
>
>     End If
>
> End SyncLock
> </code>

In my not-particularly-expert opinion, this looks OK.

[snip]
> 4)  The _eventList object is referenced by the producer class (not
> listed here), where Enqueue is called inside a SyncLock block.
>
> MY PROBLEM:
> Thing is, it seems that "_eventList.count > 0" returns true, and still
> is just before the dequeue call, but after the dequeue call it goes to
> 0, and "item" is NOTHING!!
>
> I have no idea why this could be.  Can anyone help!?!

I think you're going to have to have a look at the enqueueing code, and
if you can't find the problem, post some of it. The key point which you
may need reminding of (from the docs, my emphasis):


Queue.Enqueue Method

Adds an object to the end of the Queue.

Public Overridable Sub Enqueue( _
   ByVal obj As Object _
)

Parameters
obj
The object to add to the Queue. ****The value can be a null reference
(Nothing in Visual Basic). ****



My example code:

    Sub Main()
        Dim q As New Queue

        q.Enqueue("apple")
        q.Enqueue("banana")
        q.Enqueue(Nothing)
        q.Enqueue("pear")

        Dim o As Object
        Do While q.Count > 0
            o = q.Dequeue
            ' everything implements ToString, right?
            Console.WriteLine(o.ToString)
            ' OOPS
        Loop

        Console.ReadLine()
    End Sub

--
Larry Lard
Replies to group please
Author
9 May 2006 7:15 AM
Barney
Yeah, you're right.  I worked out what it was yesterday afternoon
eventually.

I was adding the result of a call to a factory class method to the
queue, but the factory was returning Nothing.

Put null in, get null out.  Simple.

Thanks for the reply, one's never guaranteed one in here, so many
people with so many problems an' all that. :-)

<Shipcreak />