.NET - Queue.Enqueue方法线程是否安全?

时间:2009-11-02 18:25:10

标签: .net vb.net multithreading thread-safety

假设我有一个包含队列的模块。

对于Enqueue的其他实体,它们必须通过一个函数:

public sub InsertIntoQueue(Obj)
    MyQueue.Enqueue(Obj)
end sub

如果我有多个线程正在运行并且想要调用InsertIntoQueue(),这被认为是线程安全的吗?

我的印象是,在执行InsertIntoQueue()函数所需的内存中只有一个指令副本...这会让我认为这是线程安全的。

但是,我想知道当两个线程同时尝试运行该函数时会发生什么?

这个线程是否安全,如果没有,我怎样才能使线程安全? (以及关于速度和内存使用的性能影响)

6 个答案:

答案 0 :(得分:8)

使用Queue.Synchronized包装。

答案 1 :(得分:4)

这不是线程安全的。

  

此类型的公共静态(在Visual Basic中为Shared)成员对于多线程操作是安全的。实例成员不保证是线程安全的。

来自MSDN Site

我建议添加一个对象来表示对象的同步句柄

Dim SyncHandle as Object = new Object()

并修改您的方法

Public Sub InsertIntoQueue(Object item)
    SyncLock SyncHandle 
       MyQueue.Enqueue(item)
    End SyncLock
End Sub

答案 2 :(得分:2)

SyncLock MyQueue
   MyQueue.Enqueue(Obj)
End SyncLock

End Sub

答案 3 :(得分:1)

一组指令并不意味着它是线程安全的。就指令而言,你总是只有一套。

现在,查看您提供的代码示例,无法确定它是否是线程安全的。所有标准.NET集合(包括Queue)本身都不是线程安全的,但是可以访问自己的同步版本。

现在在性能方面,当然会有性能损失,有多大 - 这取决于锁的范围和其他一些东西。特别是在Web应用程序中使用全局锁定可能会成为负载下的严重瓶颈

答案 4 :(得分:0)

冒着稍微偏离OP主题的风险,应该考虑队列的其他方法。我假设至少有一个线程出队对象,你可能还在检查队列是否为空?

从出列的角度看,如果你有多个线程出队,并且在调用dequeue之前检查队列是否为空(以防止无效的操作异常),那么一个线程(线程a)可能完全可能将队列中的最后一项出列,在另一个线程(线程b)之间读取队列不为空并且调用dequeue,因此线程a将导致无效操作异常。

你可以将支票放在支票周围为空,然后出队以解决这个问题。

Thisthis是有关线程安全性的有趣文章,我也建议您阅读this和/或this,但它们并非都在vb中。他们详细解释了线程。

答案 5 :(得分:0)

我不是线程安全方面的专家,但我正在尽可能多地学习这个问题。

我曾经认为(像你一样)这个操作可以是线程安全的,就像写入不同线程上的队列数据一样,并且没有出现队列号。但正如有人在这里解释的那样(并且在MSDN文档中无处不在):

  

公共静态(在Visual Basic中共享)   这种类型的成员是安全的   多线程操作。例   会员不能保证   线程安全的。

这意味着可能在内部MyQueue.Enqueue(Obj)就像这样:

  1. 将数据放在Queue()上;
  2. 扩充队列指针;
  3. 如果以这种方式完成,你将遇到线程问题,因为你可以看到你可以用两个线程覆盖队列中的相同位置,因为一个是在其他人做了相同的但仍然无法写入已经递增指针。

    考虑到这一点,你有几个选项,如此处所述,使用Queue.Synchronized()锁定Enqueue()方法,或者甚至更简单但对性能影响更大,在访问Queue对象时锁定私有属性以这种方式(因为你对Queue做的任何事情都不是线程安全的):

    Private ReadOnly Property MyQueue() as Queue
    Get
        SyncLock (m_myQueueLock)
            Return m_myQueue
        EndSyncLock
    End Get
    End Property
    

    希望这有帮助!