如果您有多个线程,则您的Queue 对象实例需要线程安全和同步。为了避免重新发明轮子并自己动手,我建议使用微软的ConcurrentQueue。
MSDN:
https://docs.microsoft.com/en-us/dotnet/api/system.collections.queue?view=netframework-4.7.1
如果你需要访问 ConcurrentQueue 或 ConcurrentStack
同时从多个线程收集。
如果您使用Queue(即不是ConcurrentQueue)和多个线程更新对象实例,您可能会遇到运行时异常,例如:
- ArgumentOutOfRangeException
- ArgumentException (InvalidOffLen)
- ExceptionResource.InvalidOperation_EmptyQueue
如果队列的内部状态正在修改但由于 CPU 线程调度尚未完成,则可能出现异常。如果另一个线程访问处于不一致状态的 Queue 对象实例,您可能并且很可能会遇到这些异常。
要审查的源代码:
.Net 框架 4.7.1
https://referencesource.microsoft.com/#System/compmod/system/collections/generic/queue.cs
示例控制台应用程序:
作为您的实验室运行以下命令,您应该会遇到 System.InvalidOperationException
System.InvalidOperationException: '集合在枚举器被实例化后被修改。'
class Program
{
static Queue<string> Queue = new Queue<string>();
static void Main(string[] args)
{
Thread producer = new Thread(Enqueue);
Thread consumer = new Thread(Dequeue);
producer.Start();
consumer.Start();
Console.ReadKey();
}
static void Enqueue()
{
for (int i = 0; i < 10000; i++)
{
Queue.Enqueue("Number : " + i);
SimulateWork();
}
}
static void Dequeue()
{
while (true)
{
if (Queue.Any())
{
Console.WriteLine(Queue.Dequeue());
SimulateWork();
}
}
}
static void SimulateWork()
{
for (int i = 0; i < 1000000; i++)
{ }
}
}
本实验演示了当访问处于不一致状态的 Queue 实例时会发生什么。即使只有一个生产者和一个消费者,您也需要适当的同步。
如果您围绕Enqueue 和Dequeue 操作添加锁定或同步,您会发现它运行没有问题。
lock (lockObject)
{
Queue.Enqueue("Number : " + i);
SimulateWork();
}
lock (lockObject)
{
if (Queue.Any())
{
Console.WriteLine(Queue.Dequeue());
SimulateWork();
}
}
话虽如此,我不建议您手动添加会阻塞的锁。这更像是一个实验室练习,可帮助您了解为什么。
Microsoft 已投入大量时间为我们提供线程安全集合,例如使用细粒度锁定和无锁机制的 ConcurrentQueue。
一些并发集合类型使用轻量级
SpinLock、SpinWait、SemaphoreSlim 等同步机制,
和 CountdownEvent,它们是 .NET Framework 4 中的新功能。这些
同步类型通常使用短暂的忙旋转
在他们将线程置于真正的等待状态之前。当等待时间
预计很短,旋转的计算量要少得多
比等待更昂贵,这涉及昂贵的内核转换。
对于使用旋转的集合类,这种效率意味着
多个线程可以以非常高的速率添加和删除项目。为了
有关旋转与阻塞的更多信息,请参阅 SpinLock 和
旋转等待。
如上所述,如果您有多个线程,则您的Queue 对象实例需要线程安全和同步。为了避免重新发明轮子并自己动手,我强烈建议使用 Microsoft 的 ConcurrentQueue。
参考资料:
线程安全集合:
https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/
锁定关键字
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement
线程:
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/threading/index