Producing and consuming infinite Tasks in C#
Let's say we wanted to run this forever.
private static async Task DoTheThing()
{
await Task.Delay(2000);
await Console.Out.WriteLineAsync($"{DateTime.Now.Ticks}");
}
Since it returns a task, we could generate an infinite number of tasks by yield-returning it from a loop:
private static IEnumerable<Task> ProduceForever(CancellationToken cancel)
{
do
{
yield return DoTheThing();
} while (!cancel.IsCancellationRequested);
}
And consume them as they're being generated:
private static async Task ConsumeTasks(CancellationToken cancel)
{
foreach (var task in ProduceForever(cancel))
{
await task;
}
}
Disclaimer: I probably wouldn't do this for real code. But it's still fun. Here's the whole code:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace InfiniteProducer
{
class Program
{
private static async Task Main(string[] args)
{
// Jetbrains rider needs this
while (Console.KeyAvailable) Console.ReadKey(true); // Ref: https://stackoverflow.com/a/13440920/1678053
// Create a cancellation token source to get a cancellation token
var cts = new CancellationTokenSource();
// Create a ConsumeTasks task
// It's probably running right now...
var task = ConsumeTasks(cts.Token);
// Let the user know we're ready to quit whenever they are
Console.WriteLine("Press any key to quit...");
Console.ReadKey(intercept: true);
// We quit by cancelling the task
cts.Cancel();
// We wait for the task to gracefully quit
await task;
}
private static async Task ConsumeTasks(CancellationToken cancel)
{
foreach (var task in ProduceForever(cancel))
{
await task;
}
}
private static IEnumerable<Task> ProduceForever(CancellationToken cancel)
{
do
{
yield return DoTheThing();
} while (!cancel.IsCancellationRequested);
}
private static async Task DoTheThing()
{
await Task.Delay(2000);
await Console.Out.WriteLineAsync($"{DateTime.Now.Ticks}");
}
}
}
Sidenote: I've been abusing the cancellation token here. cancellationToken.Cancel()
should cause the task to end with a Canceled
state, not a RanToCompletion
state.