Комментарии 18
Я бы все-таки отказался от
Вместо TaskCompletionSource в
new TItemQueue()
в сторону защищенного конструктора. Просто потому что может понадобиться передать в конструктор внутренней коллекции несколько параметров. Тот факт, что с внутренней коллекцией теоретически смогут работать без нашего ведома — не страшен, надо просто запретить такую работу в документации, а кто так сделает — тот сам себе злобный буратино. В конце концов, это можно будет сделать только тому, кто явно наследуется от AsyncCollection — а наследуясь от библиотечного класса надо всегда понимать, что ты делаешь.Вместо TaskCompletionSource в
_awaiterQueue
лучше хранить обобщенный делегат вида bool ItemConsumer<TItem>(TItem value)
. Это позволит очень просто реализовать операцию TakeFromAny
.Я бы все-таки отказался от new TItemQueue() в сторону защищенного конструктора.
Мысль про protected конструктор интересная. Явно проще и элегантнее, чем для передачи пачки параметров писать враппер, реализующий IProducerConsumerCollection. Я подумаю ;)
Вместо TaskCompletionSource в _awaiterQueue лучше хранить обобщенный делегат вида bool ItemConsumer<TItem&rt;(TItem value). Это позволит очень просто реализовать операцию TakeFromAny.
Я думаю в направлении interface IConsumer с похожим методом — навскидку кажется, что так реализация TakeAnyAsync() получится немного чище. Но пока не берусь утверждать как будет на самом деле, идея пока на стадии обкатывания в голове.
На самом деле получилось не очень хорошо — к сожалению, нельзя так просто взять и отменить поглощение элемента, когда очередь наполнена (элементы доминируют). В итоге, не обошлось без небольшой блокировки.
Но зато то, что получилось — работает.
Но зато то, что получилось — работает.
а родной Microsoft TPL Dataflow чем не угодил?
Очевидно, что автору требовалась просто асинхронная коллекция — а не целый фреймворк.
Не быстрее BlockingCollection — возможно, внутри она и есть.HellBrick.AsyncCollections.AsyncQueue : 13ms Nito.AsyncEx.AsyncCollection : 363ms System.Concurrent.BlockingCollection : 23ms System.Threading.Tasks.Dataflow.BufferBlock : 22ms
Производительность у библиотеки Dataflow может не рекордная (но вполне приличная). Но она позволяет строить произвольные цепочки обработки данных. Например, как в обсуждаемой статье, очередь с обработкой пачками с помощью BatchBlock.
Он не то чтобы принципиально не угодил, я с интересом к нему присматриваюсь. Но пока сложилось впечатление, что оптимальное использование DataFlow — выстроить на нём весь пайплайн обработки даннных, что немного оверкилл для моих целей.
Я, конечно же, ничего не знаю про всю вашу систему в целом. Но, мне кажется, излишеством и перебором является написание велосипедов, подобных обсуждаемому. А библиотека Dataflow довольна проста. После понимания нескольких базовых классов, её внедрение обычно сводится к простому вырезанию ненужных кусков вашего кода.
Вполне возможно, что вы правы и in the long run переписать всё на DataFlow было бы оптимальнее. С другой стороны, в процессе пришлось узнать много интересного про мемори барьеры и иже с ними, так что как минимум академический смысл во всей этой затее присутствует.
Пользуясь случаем: а в DataFlow есть аналог очереди с приоритетами? Беглый просмотр неймспейса на MSDN результата не дал.
Пользуясь случаем: а в DataFlow есть аналог очереди с приоритетами? Беглый просмотр неймспейса на MSDN результата не дал.
Вы начали изобретать велосипед lock-free коллекции, или раньше имели дело с ними и просто решили реализовать на .NET?
Если lock-free и wait-free для Вас незнакомое слово, то настоятельно рекомендую ознакомиться с различными статьями (есть даже на Хабре) на эту тему — Вам понравится!
Если lock-free и wait-free для Вас незнакомое слово, то настоятельно рекомендую ознакомиться с различными статьями (есть даже на Хабре) на эту тему — Вам понравится!
Дочитав до строк
сразу вспомнил AsyncProducerConsumerCollection из небольшой книги от Stephen Toub Task-based Asynchronous Pattern. Кстати, ссылку на эту книгу я нашёл у того же Stephen Cleary в его статье 2012 года. Странно почему Stephen Cleary не реализовал подобные быстрые асинхронные коллекции в Nito.AsyncEx.
Если происходит вызов Add(), у нас появляется новый элемент, с помощью которого можно мгновенно взять один из awaiter-ов и завершить его.
сразу вспомнил AsyncProducerConsumerCollection из небольшой книги от Stephen Toub Task-based Asynchronous Pattern. Кстати, ссылку на эту книгу я нашёл у того же Stephen Cleary в его статье 2012 года. Странно почему Stephen Cleary не реализовал подобные быстрые асинхронные коллекции в Nito.AsyncEx.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
AsyncCollections: история одного велосипеда