Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/vb.net/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Vb.net .NET多线程变量访问_Vb.net_Multithreading - Fatal编程技术网

Vb.net .NET多线程变量访问

Vb.net .NET多线程变量访问,vb.net,multithreading,Vb.net,Multithreading,我有一个4线程的应用程序。(GUI、控制器、生产者、消费者) GUI是不言自明的 控制器在一些初始设置之后启动生产者和消费者线程 制作人创建项目并将其放置在“环形缓冲区”中的空闲插槽中 消费者从“环形缓冲区”中取出项目并将其写入磁盘 生产者创造物品的速度远远高于消费者 消费者是IO重的和IO绑定的 目前,我正在检查每个环形缓冲槽中的一个变量,以确定它是否可以写入 if Slot.Free then Write Slot.Data To Disk end if 如果有空的话 将插槽数据写入磁盘

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

GUI是不言自明的

控制器在一些初始设置之后启动生产者和消费者线程

制作人创建项目并将其放置在“环形缓冲区”中的空闲插槽中

消费者从“环形缓冲区”中取出项目并将其写入磁盘

生产者创造物品的速度远远高于消费者

消费者是IO重的和IO绑定的

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

if Slot.Free then Write Slot.Data To Disk end if 如果有空的话 将插槽数据写入磁盘 如果结束 我没有使用lock/synclock,而是读取/写入插槽的“free”变量的值。我不相信这是正确的,即使它是一个易变的读/写。是否有更好的方法读取/写入此变量?变量的类型为“integer”,可以是0或1。

任何时候,如果数据可以被多个线程访问,则必须编写代码,以假定它将被多个线程访问。“墨菲定律”规定了多线程场景


例如,如果一个线程正在填充插槽,而另一个线程正在清空插槽,则需要在其周围设置锁。

有几种方法可以安全地执行此操作

  • 如果您的体系结构和需求允许,您可以使用自定义事件,这样一个线程就可以简单地向另一个侦听线程发送信号,通知变量状态已更改。但是,您必须跟踪谁在消费什么事件,以及这些消费者是否对变量是只读的,或者是读/写的
  • 您还可以在变量类型周围使用一个简单的自定义包装类(或使用泛型),为您锁定/解锁代码。在VB.NET中,我发现使用Monitor类锁定私有实例变量非常方便
  • 互斥体和信号量-.NET有一个互斥体类和一个信号量类。这两者都有助于控制对线程共享变量的访问。我喜欢互斥体,因为它们非常容易使用,而且您不需要跟踪有多少线程可以访问给定的资源
请注意,尽管一些MSDN文档声称读取或写入值类型(整型、双精度等)是一种原子操作,因此是“线程安全的”,但在实际的VB代码中这很少是正确的。像X=Y这样的简单语句实际上并不是原子的,因为在这里您必须执行两个操作-首先,加载Y的值,然后设置X的值。像这样的小事情让我毛骨悚然:)

无论您决定使用什么方法,我发现在代码中自由的注释描述在什么时候谁有权访问什么资源是非常宝贵的-三个月后,您将不会记住此代码和注释的要点

祝你好运

您可以使用s解决生产者-消费者问题:

static void Main(string[] args)
{ new Thread(Producer).Start(); new Thread(Consumer).Start(); }

static int K = 10;
static n = new Semaphore(0, K), e = new Semaphore(K, K);
static int[] buffer = new int[K];
static int _in, _out;

static void Producer()
{
    while (true)
    {
        e.WaitOne();
        buffer[_in] = produce();
        _in = (_in + 1) % buffer.Length;
        n.Release();
    }
}

static void Consumer()
{
    while (true)
    {
        n.WaitOne();
        var v = buffer[_out];
        _out = (_out + 1) % buffer.Length;
        e.Release();

        consume(v);
    }
}

您可以通过联锁操作进行检查,消除任何问题:

 const FREE = 0;
 const FILLING = 1;
 const READY = 2;
 const DRAINIG = 3;
制作人:

if (FREE == Interlocked.CompareExchange(ref slotFlag, FILLING, FREE))
{
   fillslot();
   old = Interlocked.Exchange(ref slotFlag, READY);
   Debug.Assert (FILLING == old);
}
消费者:

if (READY == Interlocked.CompareExchange(ref slotFlag, DRAINIG, READY))
{
    drain_slot();
    old = Interlocked.Exchange(ref slotFlag, FREE);
    Debug.Assert( DRAINIG == old);
 }

如果您有多个内核、多个生产者和/或多个消费者,那么这是无锁的,而且非常有效。这个问题,也就是使用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

实际上,由于他使用布尔值,并且只有一个生产者和消费者,所以只要您将数据和布尔值设置为正确的顺序,就不需要严格地使用锁。我并不是说这是一种方式:)这是CLR做出的保证吗?嗯,在VB.NET中似乎没有与
volatile
等价的东西。没有它,CLR在技术上可以自由更改执行顺序。我怀疑它真的会,但这不能保证。嗨。谢谢你的示例代码。我注意到所有插槽都有一个读锁和一个写锁。是否有理由不在每个插槽中设置一个读锁和一个写锁?锁的存在主要是因为需要更新
\u head
\u tail
变量。插槽的使用顺序是固定的,因此信号量足以确保插槽可以读取或写入。“s”信号量的用途是什么?信号量
s
旨在确保缓冲区操作(读取/写入)不重叠。但你是对的,在这种情况下这并不是必要的,所以我相应地编辑了我的答案。谢谢