Skip to main content
 首页 » 编程设计

vb.net之.NET 多线程变量访问

2024年02月20日31开发

我有一个有 4 个线程的应用程序。 (GUI、 Controller 、生产者、消费者)

GUI 的含义是不言自明的。

Controller 在进行一些初始设置后启动生产者和消费者线程。

生产者创建项目并将它们放置在“环形缓冲区”的空闲槽中

消费者从“环形缓冲区”中获取项目并将它们写入磁盘。

生产者创造元素的速度比消费者高得多。

消费者是 IO 密集型且受 IO 限制的。

目前我正在检查每个环形缓冲区槽中的变量以确定是否可以写入它。

if Slot.Free then 
  Write Slot.Data To Disk 
end if 

我没有使用锁/同步锁,而是只是读取/写入插槽的“自由”变量的值。我不认为这是正确的,即使它是 volatile 读/写。有没有更好的方法来读/写这个变量?该变量的类型为“整数”,并且为 0 或 1。

请您参考如下方法:

您提到使用环形缓冲区,但(正确实现的)环形缓冲区将能够确定它是否已满,而无需检查其所有元素,从而无需在每个插槽中使用 bool 值。

我不习惯 VB.NET,但这应该是环形缓冲区的一个工作(如果粗略)实现,当相应的写入/读取操作已满/空时,该缓冲区会阻塞。

Friend Class RingBuffer(Of T) 
    Private _slots() As T 
    Private _head As Integer 
    Private _tail As Integer 
 
    Private _readableSlots As Semaphore 
    Private _readLock As Object 
    Private _writableSlots As Semaphore 
    Private _writeLock As Object 
 
    Public Sub New(ByVal size As Integer) 
        ReDim _slots(size - 1) 
 
        _head = 0 
        _tail = 0 
        _readLock = New Object 
        _writeLock = New Object 
 
        _readableSlots = New Semaphore(0, size) 
        _writableSlots = New Semaphore(size, size) 
    End Sub 
 
    Public Function Dequeue() As T 
        Dim item As T 
        _readableSlots.WaitOne() 
        SyncLock _readLock 
            item = _slots(_head) 
            _head = (_head + 1) Mod _slots.Length 
        End SyncLock 
        _writableSlots.Release() 
        Return item 
    End Function 
 
    Public Sub Enqueue(ByVal item As T) 
        _writableSlots.WaitOne() 
        SyncLock _writeLock 
            _slots(_tail) = item 
            _tail = (_tail + 1) Mod _slots.Length 
        End SyncLock 
        _readableSlots.Release() 
    End Sub 
End Class 

一旦你有了这个,你的生产者和消费者就会变得非常愚蠢:)但是,如果你有多个消费者,则不能完全保证项目按顺序处理:

Private _buffer As RingBuffer(Of Integer) = New RingBuffer(Of Integer)(5) 
 
Private Sub Producer() 
    Dim i As Integer = 0 
    Do While True 
        _buffer.Enqueue(i) 
        i = i + 1 
    Loop 
End Sub 
 
Private Sub Consumer() 
    Do While True 
        Debug.WriteLine(("Consumer A: " & _buffer.Dequeue)) 
        Thread.Sleep(1000) 
    Loop 
End Sub