.NET终止线程有序 [英] .NET Terminating Threads in an orderly fashion

查看:93
本文介绍了.NET终止线程有序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前,我有一个由生产者和使用者线程运行的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屋!

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