Click here to Skip to main content
15,880,279 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hello,
I am using SyncLock to synchronize threads access to the returnedDataQueue object, the queue is public property, I know it's not a good practice to lock public variables but I don't know how to accomplish my goal without doing it.
I have 2 threads accessing the same queue, the first thread (ListenerThread) listens to TCP transactions that comes from the server, then it queues them into the returnedDataQueue, the second thread dequeues from the returnedDataQueue.

The Listener Thread:
------------
VB
Public Class ConnectionHandler
    Inherits TcpClient

    Public Event dataArrived()

    Private listenThread As Thread

    Private _returnedDataQueue As Queue
    Public Property returnedDataQueue() As Queue
        Get
            Return Queue.Synchronized(_returnedDataQueue)
        End Get
        Set(ByVal value As Queue)
            _returnedDataQueue = value
        End Set
    End Property

    Sub New(ByVal ip As String, ByVal port As Integer)

        returnedDataQueue = New Queue
        listenThread = New Thread(AddressOf listen) 
        listenThread.Name = "ListenThread"
        listenThread.SetApartmentState(Threading.ApartmentState.STA) 
        listenThread.Start()
    End Sub

    Public Sub listen()
        Dim networkStream As NetworkStream
        Do While Not MyBase.Connected
            Me.connect()
        Loop

        Dim strData As String
        Do While MyBase.Connected
            If networkStream.CanRead Then
                Dim bytes(MyBase.ReceiveBufferSize) As Byte
                networkStream.Read(bytes, 0, CInt(MyBase.ReceiveBufferSize))
                ' Read the NetworkStream into a byte buffer.
                strData = Encoding.ASCII.GetString(bytes).Trim

                SyncLock returnedDataQueue.SyncRoot
                    returnedDataQueue.Enqueue(strData)
                End SyncLock

                RaiseEvent dataArrived()
            End If
        Loop
    End Sub
End Class


In the main thread, I handle the dataArrived() events:
Dequeu Thread:
-------------

VB
Public Class MyService
    Inherits System.ServiceProcess.ServiceBase

    Private WithEvents objConnectionHandler As ConnectionHandler
    Private objUdpClient As UDPMulticaster
	
    Protected Overrides Sub OnStart(ByVal args() As String)
        objUdpClient = New UDPMulticaster(settings.MulticastIP, settings.MulticastPort, settings.MulticastPortEP)
        objConnectionHandler = New ConnectionHandler(settings.IP, settings.Port)
    End Sub

    Private Sub objConnectionHandler_dataArrived() Handles objConnectionHandler.dataArrived
        Try
            SyncLock objConnectionHandler.returnedDataQueue.SyncRoot
                While objConnectionHandler.returnedDataQueue.Count > 0
                    Dim strTemp As String = objConnectionHandler.returnedDataQueue.Dequeue
                    Dim success As Boolean = CBool(wcfService.ProcessTransaction(strTemp))
                    If success Then
                        Dim seq As Integer = CInt(wcfService.getSequenceNumber())
                        objUdpClient.multicastToNetwork(strTemp, seq)
                    End If
                End While
            End SyncLock
        Catch ex As Exception
             'Write Log
        End Try
    End Sub
End Class


Suddenly, the ListenThread stops working, I don't get any errors, it looks that the code is not executing anymore and the thread is waiting for something, after running the code for couple of days on a busy system, I believe the Dequeue Thread locks the returnedDataQueue for long time, then the listen thread won't be able to queue any new transactions!

Is there anything wrong with the code? is there any alternative approach to prevent the threads from accessing the queue simultaneously?

Thanks,
Sam
Posted
Updated 20-Jun-12 5:22am
v2

1 solution

This design is a bit hard to follow. I would advise to use a bit different approach, without using async API. Instead, the application which listens for new connections and performs data exchange on the network (reading/writing from/to network stream or reading/writing form/to a remote socket) should do it in separate threads, both using blocking call. You see, the sockets themselves serve as a synchronization primitive, you can even implement a lock or a mutex using pure socket API. (Historically, threads were initially developed as IPC objects, not for network communications.) However, you will need lock to share some collection where you store the remote sockets or remote TcpClient instances.

Due to simplicity of such primarily sequential design, you have no danger of deadlock while having less code and more control. You can find some ideas in my past answer:
Multple clients from same port Number[^].

As to the blocking queue, you can refer to my well-tested and explained implementation (sorry, it is C#):
Simple Blocking Queue for Thread Communication and Inter-thread Invocation[^].

—SA
 
Share this answer
 
v2
Comments
Maciej Los 20-Jun-12 15:39pm    
Wow! Maestro!
Sergey Alexandrovich Kryukov 20-Jun-12 15:42pm    
Thank you so much, Maciej.
--SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900