.NET终止线程有序 [英] .NET Terminating Threads in an orderly fashion
问题描述
目前,我有一个由生产者和使用者线程运行的RingBuffer.
Currently, I have a RingBuffer which is run by a producer and a consumer thread.
在寻找一种有序地终止它们的方法时,我认为我会使用一个标志来指示生产者何时完成,然后在我的使用者中检查该标志以及需要写入的环形缓冲区的数量.如果生产者完成并且环形缓冲区没有需要写入的插槽,则消费者可以终止.
In looking for a method of terminating them orderly, I thought I'd use a flag to indicate when the producer had finished and then check that flag in my consumer along with the number of ring buffer slots that need to be written. If the producer has finished and the ring buffer has no slots that need to be written the consumer can terminate.
那很好.
但是,如果我通过插入睡眠来人为延长生产者的时间,则消费者不会终止.我相信这是信号灯被使用的结果.
However, if I artificially lengthen the time the producer takes by inserting a sleep, the consumer does not terminate. I believe this is a consequence of the semaphores being used.
这是我正在使用的代码.请注意,写入所有插槽后,程序将挂起".生产者终止,但消费者挂起".
Here is the code I'm working with. Notice that the program will "hang" after all slots have been written. The producer terminates, but the consumer "hangs".
任何有条不紊地终止这一切的建议将不胜感激.
Any advice on terminating both in an orderly fashion would be greatly appreciated.
编辑-更新了代码,其中包含Henk建议使用队列的建议. +1000指向第一个人建议终止消费者/生产者线程的更好方法,而不是知道正在使用的物品的确切数量或返回值,例如null/nothing (空/无),表示队列中不存在任何项目(尽管这并不意味着它们仍在生产中.)
Edit - Updated code with Henk's suggestion of using a Queue. +1000 points to the first person to suggest a better method of terminating the consumer/producer threads than either knowing the exact amount of items being worked with or returning a value such as null/nothing indicating that no more items exist in the queue (though this doesn't mean they aren't still being produced.)
编辑-我相信我已经解决了.只需将每个null或不传递任何东西给RingBuffer.Enqueue给每个使用者,并在使用者中捕获null或不传递任何对象以终止它.希望有人觉得这有用.
Edit - I believe I've figured it out. Simply pass null or nothing to RingBuffer.Enqueue for each consumer and catch the null or nothing object in the consumer to terminate it. Hopefully someone finds this useful.
Imports System.Collections
Module Module1
Public Class RingBuffer
Private m_Capacity As Integer
Private m_Queue As Queue
Public Sub New(ByVal Capacity As Integer)
m_Capacity = Capacity
m_Queue = Queue.Synchronized(New Queue(Capacity))
End Sub
Public Sub Enqueue(ByVal value As Object)
SyncLock m_Queue.SyncRoot
If m_Queue.Count = m_Capacity Then
Threading.Monitor.Wait(m_Queue.SyncRoot)
End If
m_Queue.Enqueue(value)
Threading.Monitor.PulseAll(m_Queue.SyncRoot)
End SyncLock
End Sub
Public Function Dequeue() As Object
Dim value As Object = Nothing
SyncLock m_Queue.SyncRoot
If m_Queue.Count = 0 Then
Threading.Monitor.Wait(m_Queue.SyncRoot)
End If
value = m_Queue.Dequeue()
Console.WriteLine("Full Slots: {0} - Open Slots: {1}", m_Queue.Count, m_Capacity - m_Queue.Count)
Threading.Monitor.PulseAll(m_Queue.SyncRoot)
End SyncLock
Return value
End Function
End Class
Public Class Tile
Public buffer() As Byte
Public Sub New()
buffer = New Byte(1023) {}
End Sub
End Class
Public Sub Producer(ByVal rb As RingBuffer)
Dim enq As Integer = 0
Dim rng As New System.Security.Cryptography.RNGCryptoServiceProvider
For i As Integer = 0 To 1023
Dim t As New Tile
rng.GetNonZeroBytes(t.buffer)
rb.Enqueue(t)
enq += 1
Threading.Thread.Sleep(10)
Next i
rb.Enqueue(Nothing)
Console.WriteLine("Total items enqueued: " & enq.ToString())
Console.WriteLine("Done Producing!")
End Sub
Public Sub Consumer(ByVal rb As RingBuffer)
Dim deq As Integer = 0
Using fs As New IO.FileStream("c:\test.bin", IO.FileMode.Create)
While True
Dim t As Tile = rb.Dequeue()
If t Is Nothing Then Exit While
fs.Write(t.buffer, 0, t.buffer.Length)
deq += 1
Threading.Thread.Sleep(30)
End While
End Using
Console.WriteLine("Total items dequeued: " & deq.ToString())
Console.WriteLine("Done Consuming!")
End Sub
Sub Main()
Dim rb As New RingBuffer(1000)
Dim thrdProducer As New Threading.Thread(AddressOf Producer)
thrdProducer.SetApartmentState(Threading.ApartmentState.STA)
thrdProducer.Name = "Producer"
thrdProducer.IsBackground = True
thrdProducer.Start(rb)
Dim thrdConsumer As New Threading.Thread(AddressOf Consumer)
thrdConsumer.SetApartmentState(Threading.ApartmentState.STA)
thrdConsumer.Name = "Consumer"
thrdConsumer.IsBackground = True
thrdConsumer.Start(rb)
Console.ReadKey()
End Sub
End Module
推荐答案
如果我查看Consumer函数:
If I look at the Consumer function:
If rb.FullSlots = 0 And Threading.Interlocked.Read(ProducerFinished) = 0 Then
Exit While
End If
Dim t As Tile = rb.Read()
使用者可以找到rb.FullSlots = 0,但ProducerFinished = False,然后继续Read().在Read()内部,它等待writerSemaphore,但是与此同时,生产者可以完成并且永远不会释放writerSemaphore.
The consumer could find rb.FullSlots = 0 but ProducerFinished = False and continue to Read(). Inside Read() it waits for the writerSemaphore but in the mean time the Producer could finish and never release the writerSemaphore.
因此,(至少)制作人应该采取措施让读者在减少ProducerFinished之后继续播放.
So (at least) the producer should take steps to let the readers continue after it decreases the ProducerFinished.
但是我认为,如果将此关闭"逻辑移至Ring缓冲区,则会得到更好的设计.在那里,您可以将其与数据可用逻辑结合起来.
But I think you get a better design if you move this 'Closing' logic to the Ring buffer. There you can combine it with the Data-available logic.
这篇关于.NET终止线程有序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!