I've created this program to show how to stop threads in a controlled manner.Important points in the program:Starting and waiting for threadsException handlingStopping threads in a controlled manner via the CancellationToken. Overall simplicity and correctness.The scenario3 tasks with access to the same List that keep working on the list for 10 seconds and then stop. The 3rd task throws an exception. The outputA is working. Job number: 0B is working. Job number: 0C throwing after 1000msA is working. Job number: 1A is working. Job number: 2B is working. Job number: 1A is working. Job number: 3B is working. Job number: 2A is working. Job number: 4Oh no. System.AggregateException thrown: One or more errors occurred.Exceptions:        C throwing after 1000msElapsed 10228ms8,3,4,2,7,0,0,2The programusing System;using System.Collections.Generic;using System.Diagnostics;using System.Linq;using System.Text;using System.Threading;using System.Threading.Tasks;namespace TwoThreadsFor10Secs{    class GoForTenSeconds    {        private object _lock = new object();        private List<int> _list = new List<int>();        private Random _rnd = new Random();        public List<int> List {            get {                lock (_lock)                {                    return _list.ToList();                }            }        }        private async Task DoWorkAsync(string taskId, CancellationToken cancelToken, int sleepInMs)        {            int i = 0;            while (true)            {                if (cancelToken.IsCancellationRequested) return;                lock (_lock)                {                   Console.WriteLine(${taskId} is working. Job number: {i++});                    _list.Add(_rnd.Next(0, 10));                }                await Task.Delay(sleepInMs, cancelToken);            }        }        private async Task ThrowAfterAsync(string taskId, CancellationToken cancelToken, int afterMs )        {            await Task.Delay(afterMs, cancelToken);            var msg = ${taskId} throwing after {afterMs}ms;            Console.WriteLine(msg);            throw new ApplicationException(msg);        }        public async Task<List<int>> GoForNSecondsAsync( int howLongSecs)        {            using (var cancelSource = new CancellationTokenSource())            {                Task[] tasks = null;                try                {                    var cancelToken = cancelSource.Token;                    tasks = new[] {  Task.Run( async () => await DoWorkAsync(A,cancelToken,2000)),                              Task.Run(async () => await DoWorkAsync(B, cancelToken,4000)),                              Task.Run(async () => await ThrowAfterAsync(C, cancelToken, 1000))                           };                    await Task.Delay(howLongSecs * 1000);                    cancelSource.Cancel();                    await Task.WhenAll(tasks);                }                catch (Exception)                {                    foreach (var task in tasks)                    {                        var taskEx = task.Exception;                        if (taskEx != null)                        {                            Console.WriteLine($Oh no. {taskEx.GetType()} thrown: {taskEx.Message});                            if (taskEx is AggregateException)                            {                                Console.WriteLine($Exceptions:);                                var aggregateEx = taskEx as AggregateException;                                foreach (var individualEx in aggregateEx.InnerExceptions)                                {                                    Console.WriteLine(\t+individualEx.Message);                                }                            }                        }                    }                }                return _list;            }        }    }    class Program    {        static void Main(string[] args)        {            Stopwatch sw = new Stopwatch();            var list = Task.Run(async () =>            {                sw.Start();                return await new GoForTenSeconds().GoForNSecondsAsync(10);            }).Result;            var elapsedMs = sw.ElapsedMilliseconds;            Console.WriteLine($Elapsed {elapsedMs}ms);            Console.WriteLine(string.Join(,, list));            Console.ReadLine();        }    }}
Working with threads - shared access and graceful cancellation
c#;multithreading;task parallel library