I've been working on my first TPL project recently, and I'm trying to mix a couple of patterns that apparently aren't used together very much.
Given a huge volume of IO-bound http request tasks, and decent but not unlimited concurrent server capacity, I need a producer-consumer implementation that allows the blocking collection to precipitate tasks as they complete. The distinction here from the usual BlockingCollection<>
scenario is, an arbitrary Take()
method will not remove tasks in the order they complete. Essentially, I need some mix of BlockingCollection<>
, .WhenAny()
, and .Remove()
.
My options so far are:
I can kitbash a wrapper for
BlockingCollection<byte>
with a pairedConcurrentDictionary<int, Task<T>>
using theTask.Id
for the key:.ToDictionary(t => t.Id)
. The blocking collection is just a token bag for traffic management, and the dictionary provides a when-any enumerable and a remove. This works, but its a bit of duct tape & bailing wire.I could try passing
.GetConsumingEnumerable()
to.WhenAny()
, which looks great in the editor, but is bound to fail as the blocking collections gets arbitrarily consumed when the enumeration is scanned for completed tasks. I have not tried this yet, but it shouldn't work the way I intend.?
This seems to be a somewhat unusual scenario; I'm bumping the .DefaultConnectionLimit
up between 120 to 720 depending on the endpoint I'm targeting, and then spamming it with as much load as it can handle. I can find no obvious concurrent collection and/or blocking wrapper that provides .Add()
, .Remove()
, and a non-removing IEnumerable<>
together. Am I missing something?