I have a requirement to fire off web service requests to an online api and I thought that Parallel Extensions would be a good fit for my needs.
The web service in questi
I agree with others that TPL Dataflow sounds like a good solution for this.
To limit the processing, you could create a TransformBlock
that doesn't actually transform the data in any way, it just delays it if it arrived too soon after the previous data:
static IPropagatorBlock CreateDelayBlock(TimeSpan delay)
{
DateTime lastItem = DateTime.MinValue;
return new TransformBlock(
async x =>
{
var waitTime = lastItem + delay - DateTime.UtcNow;
if (waitTime > TimeSpan.Zero)
await Task.Delay(waitTime);
lastItem = DateTime.UtcNow;
return x;
},
new ExecutionDataflowBlockOptions { BoundedCapacity = 1 });
}
Then create a method that produces the data (for example integers starting from 0):
static async Task Producer(ITargetBlock target)
{
int i = 0;
while (await target.SendAsync(i))
i++;
}
It's written asynchronously, so that if the target block isn't able to process the items right now, it will wait.
Then write a consumer method:
static void Consumer(int i)
{
Console.WriteLine(i);
}
And finally, link it all together and start it up:
var delayBlock = CreateDelayBlock(TimeSpan.FromMilliseconds(500));
var consumerBlock = new ActionBlock(
(Action)Consumer,
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded });
delayBlock.LinkTo(consumerBlock, new DataflowLinkOptions { PropagateCompletion = true });
Task.WaitAll(Producer(delayBlock), consumerBlock.Completion);
Here, delayBlock
will accept at most one item every 500 ms and the Consumer()
method can run multiple times in parallel. To finish processing, call delayBlock.Complete()
.
If you want to add some caching per your #2, you could create another TransformBlock
do the work there and link it to the other blocks.