Я бы сделал это, как в следующем классе. Вы вызываете Enqueue()
, когда создаете элемент, чтобы добавить его в одну из очередей. Этот метод всегда возвращает (почти) сразу. В другой ветке вы звоните Dequeue()
, когда будете готовы потреблять предмет. Он пытается сначала взять из очереди с высоким приоритетом. Если в данный момент нет ни одной позиции ни в одной из очередей, вызов блокируется. Когда вы закончите продюсировать, вы звоните Complete()
. После того, как этот вызов сделан, и обе очереди пусты, следующий вызов (или текущий заблокированный вызов) на Dequeue()
выбрасывает InvalidOperationException
.
Если ваш производитель (производители) могут быть быстрее, чем ваши потребители в течение длительных периодов времени, вы должны ограничить очереди (new BlockingCollection<T>(capacity)
). Но в этом случае, если у вас есть только один поток, который производит элементы как с низким, так и с высоким приоритетом, возможно, что элементы с высоким приоритетом должны будут ожидать элементы с низким приоритетом. Вы могли бы исправить это, имея один поток для создания элементов с высоким приоритетом и один для элементов с низким приоритетом. Или вы можете связать только очередь с высоким приоритетом и надеяться, что вы не получите миллион наименований с низким приоритетом одновременно.
class Worker<T>
{
BlockingCollection<T> m_highPriorityQueue = new BlockingCollection<T>();
BlockingCollection<T> m_lowPriorityQueue = new BlockingCollection<T>();
public void Enqueue(T item, bool highPriority)
{
BlockingCollection<T> queue;
if (highPriority)
queue = m_highPriorityQueue;
else
queue = m_lowPriorityQueue;
queue.Add(item);
}
public T Dequeue()
{
T result;
if (!m_highPriorityQueue.IsCompleted)
{
if (m_highPriorityQueue.TryTake(out result))
return result;
}
if (!m_lowPriorityQueue.IsCompleted)
{
if (m_lowPriorityQueue.TryTake(out result))
return result;
}
if (m_highPriorityQueue.IsCompleted && m_lowPriorityQueue.IsCompleted)
throw new InvalidOperationException("All work is done.");
else
{
try
{
BlockingCollection<T>.TakeFromAny(
new[] { m_highPriorityQueue, m_lowPriorityQueue },
out result);
}
catch (ArgumentException ex)
{
throw new InvalidOperationException("All work is done.", ex);
}
return result;
}
}
public void Complete()
{
m_highPriorityQueue.CompleteAdding();
m_lowPriorityQueue.CompleteAdding();
}
}