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.