SyncLock导致死锁 [英] SyncLock causes a deadlock
问题描述
你好,
我正在使用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 remoteTcpClient
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屋!