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.