Я работаю над простым регистратором файлов, у меня были проблемы с потокобезопасностью, и я смотрел, как это делают другие. Я столкнулся с подходом к использованию BlockingCollection в качестве очереди и цикла foreach для обработки этой очереди:
var queue = new BlockingCollection<string>(1024);
var t = Task.Factory.StartNew(() => {
foreach (var message in queue.GetConsumingEnumerable()) {
WriteMessageToFile(message);
}
}, TaskCreationOptions.LongRunning);
Dim queue = New BlockingCollection(Of String)(1024)
Dim t = Task.Factory.StartNew(Sub()
For Each message In queue.GetConsumingEnumerable()
WriteMessageToFile(message)
Next
End Sub, TaskCreationOptions.LongRunning)
Этот цикл For Each на самом деле работает бесконечно. Он обрабатывает данные, которые я добавляю в queue
, пока я не вызову .CompleteAdding
в BlockingCollection.
Интересно, почему это так работает и как, и если это хороший подход. Что делает поток, пока коллекция пуста, проверяет ли он каждый тик? Разве это не ресурсоемко?
.CompleteAdding
, так что нет, ваш вопрос, похоже, звучит так: «Интересно, почему это так работает». Я говорю, что если бы не было требования вызыватьCompleteAdding()
, как еще он определил бы, что данных действительно больше нет? Что, если вы запустите задачи производителя и потребителя одновременно, и потребители попадут в коллекцию раньше, чем она будет содержать данные, или данные будут создаваться медленнее, чем потребляться? Если бы они навсегда отказались от перечисления коллекции, потому что в этот момент она оказалась пустой, это было бы не очень полезно, не так ли? - person Lance U. Matthews   schedule 10.06.2020GetConsumingEnumerator()
включает использованиеforeach
, помимо прочего, завершая перечисление только после того, какBlockingCollection<>.IsCompleted
становитсяtrue
(это означает, чтоCount
равно0
иCompleteAdding()
вызывается). Помните, что он поддерживает несколько производителей или потребителей. - person Lance U. Matthews   schedule 10.06.2020For Each
, за кулисами происходит вызов методаMoveNext
перечислителя (для продвижения вперед и проверки достижения конца) и чтение свойстваCurrent
. В случаеConsumingIterator
я ожидаю, чтоMoveNext
просто блокируется до тех пор, пока не будет что-то (или ничего) для возврата. - person Craig   schedule 10.06.2020