Можно ли положить TryDequeue в цикл while?
Я раньше не использовал параллельную очередь.
Можно ли использовать TryDequeue, как показано ниже, в цикле while? Может ли это не застрять навсегда?
var cq = new ConcurrentQueue<string>();
cq.Enqueue("test");
string retValue;
while(!cq.TryDequeue(out retValue))
{
// Maybe sleep?
}
//Do rest of code
Ответы
Ответ 1
Да, это безопасно в соответствии с документацией, но это не рекомендуемый дизайн.
Он может получить "Stuck forever", если очередь была пуста при первом вызове TryDequeue, и если ни один другой поток не подталкивает данные в очереди после этой точки (вы могли бы разбить время после попыток N или после таймаута).
ConcurrentQueue предлагает член IsEmpty для проверки наличия элементов в очереди. Гораздо эффективнее проверять его, чем перебирать вызов TryDequeue (особенно, если очередь вообще пуста)
Что вы можете сделать, это:
while(cq.IsEmpty())
{
// Maybe sleep / wait / ...
}
if(cq.TryDequeue(out retValue))
{
...
}
EDIT:
Если этот последний вызов возвращает false: другой из ваших потоков удаляет объект. Если у вас нет других потоков, это безопасно, если это так, вы должны использовать while (TryDequeue)
Ответ 2
Это безопасно в том смысле, что цикл на самом деле не заканчивается до тех пор, пока не будет извлечен элемент, и что он в конечном итоге закончится, если очередь имеет элемент, который нужно вынуть. Если очередь опустела другим потоком, и добавлено больше элементов, то, конечно, цикл не закончится.
Помимо всего этого, у вас есть цикл занятости. Этого практически всегда следует избегать. Либо вы в конечном итоге постоянно проверяете очередь, запрашивая больше предметов, тратя время и время процессора на процесс, или вы заканчиваете спать и, следовательно, фактически не используете элемент в очереди, как только он будет добавлен (и даже тогда, все еще теряя смысл некоторое время/усилие на контекст переключает только для опроса очереди).
Что вы должны делать вместо этого, если вы окажетесь в положении, когда вам нужно "подождать, пока не появится элемент для меня", используйте BlockingCollection
. Он специально предназначен для переноса различных типов параллельных коллекций и блокировки до тех пор, пока не будет доступный элемент. Это позволяет вам изменить свой код на queue.Take()
, и будет проще писать, семантически заявляя, что вы делаете, быть четко правильным, заметно более эффективным и полностью безопасным.