Cancellation in a multi-threaded Console App can be surprisingly difficult, even though I’ve done it dozens (hundreds?!?) of times before. As any programmer can tell you, there are the “Things you know” and “Things you merely think you know.” In programming, fiddly little details count.
I won’t go into the trade-offs between using Tasks or Parallel.For[Each] or TPL DataFlow, but suffice it to say that the later is both the most useful and easiest to get right. Enjoy…
using System; using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; namespace CancelDemo { class Program { static void Main(string[] args) { Console.WriteLine("Press any key to cancel..."); Console.WriteLine(); var cts = new CancellationTokenSource(); var client = new HttpClient(); try { var fetcher = new ActionBlock<string>( async url => { await client.GetAsync(url, cts.Token); Console.WriteLine($"FETCHED {url}"); }, new ExecutionDataflowBlockOptions() { CancellationToken = cts.Token, MaxDegreeOfParallelism = Environment.ProcessorCount }); urls.ForEach(url => fetcher.Post(url)); fetcher.Complete(); var readKey = new TaskFactory(cts.Token).StartNew(() => { while (!Console.KeyAvailable) Thread.Sleep(100); cts.Cancel(); Console.ReadKey(true); }); Task.WaitAny(new Task[] { fetcher.Completion, readKey }); } catch (OperationCanceledException) { } catch (Exception error) { cts.Cancel(); Console.WriteLine("Error: " + error.Message); } Console.WriteLine(); Console.Write("Press any key to terminate..."); Console.ReadKey(true); } private static List<string> urls = new List<string>() { "https://www.pinterest.com/pin/143270831869696811/", "https://www.pinterest.com/pin/155585362100632165/", "https://www.pinterest.com/pin/23010648069511614/", "https://www.pinterest.com/pin/303711568591359002/", "https://www.pinterest.com/pin/314900198915723688/", "https://www.pinterest.com/pin/316166836311260373/", "https://www.pinterest.com/pin/535576580657166380/", "https://www.pinterest.com/pin/56154326576327972/", "https://www.pinterest.com/pin/56154326576368600/", "https://www.pinterest.com/pin/88523948898601589/", "https://www.pinterest.com/pin/108438303498183811/", "https://www.pinterest.com/pin/127156389453876236/", "https://www.pinterest.com/pin/270708627574674608/", "https://www.pinterest.com/pin/368380444492800731/", "https://www.pinterest.com/pin/250653535491278041/", "https://www.pinterest.com/pin/565483296932119884/", "https://www.pinterest.com/pin/173177548141309276/", "https://www.pinterest.com/pin/430867889333901210/", "https://www.pinterest.com/pin/61361613645883874/", "https://www.pinterest.com/pin/16466354858074206/" }; } }