Если у вас более одного потока, для вашего экземпляра объекта Queue
потребуется безопасность потока и синхронизация. Чтобы не изобретать велосипед и не делать это самостоятельно, я бы предложил использовать Microsoft 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
Исключения возможны, если внутреннее состояние очереди изменяется, но еще не завершено из-за планирования потоков ЦП. Если другой поток обращается к экземпляру объекта Queue, когда он находится в несогласованном состоянии, вы можете и, вероятно, столкнетесь с этими исключениями.
Исходный код для обзора:
.Net Framework 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();
}
}
С учетом вышесказанного я НЕ предлагаю вам вручную добавить блокировки, которые будут блокировать. Это скорее лабораторное упражнение, которое поможет вам понять WHY .
Microsoft потратила много времени на предоставление нам многопоточных коллекций, таких как ConcurrentQueue
, с использованием мелкозернистых блокировок и механизмов без блокировок.
Некоторые типы одновременных коллекций используют легкий
механизмы синхронизации, такие как SpinLock, SpinWait, SemaphoreSlim,
и CountdownEvent, которые являются новыми в .NET Framework 4. Эти
Типы синхронизации обычно используют вращение в течение коротких периодов времени.
прежде чем они поместят поток в настоящее состояние ожидания. Когда время ожидания
как ожидается, будет очень коротким, вращение гораздо менее вычислительно
дороже, чем ожидание, что включает в себя дорогой переход ядра.
Для коллекционных классов, использующих спиннинг, эта эффективность означает, что
несколько потоков могут добавлять и удалять элементы с очень высокой скоростью. За
больше информации о вращении против блокировки, см. SpinLock и
SpinWait.
Как указано выше, если у вас более одного потока, это потребует безопасности потока и синхронизации для вашего экземпляра объекта 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
Threading:
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/threading/index