SyncLock导致死锁 [英] SyncLock causes a deadlock

查看:377
本文介绍了SyncLock导致死锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你好,
我正在使用SyncLock来同步对返回的DataQueue对象的线程访问,该队列是公共属性,我知道锁定公共变量不是一个好习惯,但是我不知道如何不执行它来实现我的目标. br/> 我有2个线程访问相同的队列,第一个线程(ListenerThread)侦听来自服务器的TCP事务,然后将它们排队到returnDataData队列中,第二个线程从ReturnedDataQueue出队.

侦听器线程:
------------

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:
------------

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



在主线程中,我处理dataArrived()事件:
出队线程:
-------------



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

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



突然,ListenThread停止工作,我没有收到任何错误,它看起来代码不再执行,并且线程正在等待某些东西,在繁忙的系统上运行了几天代码后,我相信Dequeue线程长时间锁定了returnDataQueue,那么侦听线程将无法对任何新事务进行排队!

代码有什么问题吗?有没有其他方法可以防止线程同时访问队列?

谢谢,
Sam



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

推荐答案

这种设计很难遵循.我建议使用一些不同的方法,而不使用异步API.取而代之的是,侦听新连接并在网络上执行数据交换(从网络流读取/写入网络流或从远程套接字读取/写入表单/到远程套接字)的应用程序应在单独的线程中执行此操作,两者均使用阻塞调用.您会看到,套接字本身用作同步原语,甚至可以使用纯套接字API实现锁或互斥锁. (从历史上看,线程最初是作为IPC对象开发的,而不是用于网络通信的.)但是,您将需要锁以共享一些集合,这些集合用于存储远程套接字或远程TcpClient实例.
由于这种主要顺序设计的简单性,因此在减少代码和控制的同时,也没有死锁的危险.您可以在我过去的答案中找到一些想法:
来自同一端口号的多个客户端 [用于线程通信和线程间调用的简单阻塞队列 [ ^ ].

—SA
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


这篇关于SyncLock导致死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆