Producing and consuming infinite Tasks in C#

Let’s say we wanted to run this forever.

1private static async Task DoTheThing()
2{
3    await Task.Delay(2000);
4    await Console.Out.WriteLineAsync($"{DateTime.Now.Ticks}");
5}

Since it returns a task, we could generate an infinite number of tasks by yield-returning it from a loop:

1private static IEnumerable ProduceForever(CancellationToken cancel)
2{
3    do
4    {
5        yield return DoTheThing();
6    } while (!cancel.IsCancellationRequested);
7}

And consume them as they’re being generated:

1private static async Task ConsumeTasks(CancellationToken cancel)
2{
3    foreach (var task in ProduceForever(cancel))
4    {
5        await task;
6    }
7}

Disclaimer: I probably wouldn’t do this for real code. But it’s still fun. Here’s the whole code:

 1using System;
 2using System.Collections.Generic;
 3using System.Threading;
 4using System.Threading.Tasks;
 5
 6namespace InfiniteProducer
 7{
 8    class Program
 9    {
10        private static async Task Main(string[] args)
11        {
12            // Jetbrains rider needs this
13            while (Console.KeyAvailable) Console.ReadKey(true); // Ref: https://stackoverflow.com/a/13440920/1678053
14            
15            // Create a cancellation token source to get a cancellation token
16            var cts = new CancellationTokenSource();
17
18            // Create a ConsumeTasks task
19            // It's probably running right now...
20            var task = ConsumeTasks(cts.Token);
21
22            // Let the user know we're ready to quit whenever they are
23            Console.WriteLine("Press any key to quit...");
24            Console.ReadKey(intercept: true);
25            
26            // We quit by cancelling the task
27            cts.Cancel();
28
29            // We wait for the task to gracefully quit
30            await task;
31        }
32
33        private static async Task ConsumeTasks(CancellationToken cancel)
34        {
35            foreach (var task in ProduceForever(cancel))
36            {
37                await task;
38            }
39        }
40        
41        private static IEnumerable ProduceForever(CancellationToken cancel)
42        {
43            do
44            {
45                yield return DoTheThing();
46            } while (!cancel.IsCancellationRequested);
47        }
48
49        private static async Task DoTheThing()
50        {
51            await Task.Delay(2000);
52            await Console.Out.WriteLineAsync($"{DateTime.Now.Ticks}");
53        }
54    }
55}

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.

<< Previous Post

|

Next Post >>

#.NET #C#