From bebad86de992f39bb91d8c8b1cef9959148ac7b0 Mon Sep 17 00:00:00 2001 From: David Hackbarth Date: Tue, 7 Jul 2026 19:28:35 +0200 Subject: [PATCH] Added advanced multithreading examples including async/await, tasks, race conditions, and cancellation handling --- Multithreading/Asyncs.cs | 60 +++++++++ Multithreading/RaceConditions.cs | 110 +++++++++++++++ Multithreading/Tasks.cs | 224 +++++++++++++++++++------------ 3 files changed, 311 insertions(+), 83 deletions(-) create mode 100644 Multithreading/Asyncs.cs create mode 100644 Multithreading/RaceConditions.cs diff --git a/Multithreading/Asyncs.cs b/Multithreading/Asyncs.cs new file mode 100644 index 0000000..96d88fc --- /dev/null +++ b/Multithreading/Asyncs.cs @@ -0,0 +1,60 @@ +// using System; +// using System.Threading.Tasks; +// +// namespace Multithreading +// { +// internal static class Program +// { +// public static void Main(string[] args) +// { +// Console.WriteLine("Program started"); +// +// Task randomNumberTask = Task.Run(() => +// { +// Console.WriteLine($"Thread Id: {Thread.CurrentThread.ManagedThreadId}"); +// var random = new Random(); +// return random.Next(); +// }); +// +// randomNumberTask.Wait(); // this will pause our main thread from running +// int result = randomNumberTask.Result; +// Console.WriteLine($"Random number: {result}"); +// +// GetRandomNumberAsync().Wait(); +// +// Console.WriteLine("Program ended"); +// } +// +// private static async Task GetRandomNumberAsync() +// { +// Task randomNumberTask = Task.Run(() => +// { +// Console.WriteLine($"Thread Id: {Thread.CurrentThread.ManagedThreadId}"); +// var random = new Random(); +// return random.Next(); +// }); +// +// int result = await randomNumberTask; +// +// return result; +// } +// +// private static async Task SleepAsync(int seconds) +// { +// Console.WriteLine($"Thread Id before sleep: {Thread.CurrentThread.ManagedThreadId}"); +// await Task.Delay(TimeSpan.FromSeconds(seconds)); +// Console.WriteLine($"Thread Id before sleep: {Thread.CurrentThread.ManagedThreadId}"); +// +// // GameObject[] prefabs; +// // for (int i = 0; i < prefabs.Length; i++) +// // { +// // Instantiate(prefabs[i]); +// // if (i % 10 == 0) +// // { +// // // update ui, e.g. progress bar +// // await Task.Delay(100); +// // } +// // } +// } +// } +// } \ No newline at end of file diff --git a/Multithreading/RaceConditions.cs b/Multithreading/RaceConditions.cs new file mode 100644 index 0000000..b6a1ba6 --- /dev/null +++ b/Multithreading/RaceConditions.cs @@ -0,0 +1,110 @@ +using System; +using System.Text; +using System.Threading.Tasks; + +namespace Multithreading +{ + internal static class Program + { + private static readonly object _fileLock = new object(); + + public static void Main(string[] args) + { + Console.WriteLine("Program started"); + + // int[] numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + // Task[] tasks = new Task[100]; + // Random random = new Random(); + // for (int i = 0; i < tasks.Length; i++) + // { + // tasks[i] = Task.Run(() => + // { + // for (int j = 0; j < numbers.Length; j++) + // { + // Console.WriteLine($"Before Thread Id: {Thread.CurrentThread.ManagedThreadId}, number({j}): {numbers[j]}"); + // + // Thread.Sleep(random.Next(0, 1000)); + // + // // not thread-safe + // // int oldValue = numbers[j]; + // // int newValue = oldValue * random.Next(10); + // // numbers[j] = newValue; + // // Thread.Sleep(random.Next(0, 1000)); + // // Console.WriteLine($"After Thread Id: {Thread.CurrentThread.ManagedThreadId}, number({j}): {numbers[j]} / oldValue: {oldValue} / newValue: {newValue}"); + // + // // making this thread-safe + // // option 1: lock + // lock (numbers) + // { + // int oldValue = numbers[j]; + // int newValue = oldValue * random.Next(10); + // numbers[j] = newValue; + // Thread.Sleep(random.Next(0, 1000)); + // Console.WriteLine($"After Thread Id: {Thread.CurrentThread.ManagedThreadId}, number({j}): {numbers[j]} / oldValue: {oldValue} / newValue: {newValue}"); + // } + // + // // option 2: use concurrent collections + // // https://learn.microsoft.com/en-us/dotnet/standard/collections/thread-safe/ + // + // // option 3: use chunks to have no overlapping + // + // } + // }); + // } + + // Dictionary dictionary = new Dictionary(); + // Task[] tasks = new Task[1000]; + // Random random = new Random(); + // for (int i = 0; i < tasks.Length; i++) + // { + // tasks[i] = Task.Run(() => + // { + // lock (dictionary) // one option, another option is to use ConcurrentDictionary + // { + // int key = random.Next(); + // if (!dictionary.ContainsKey(key)) + // { + // // dictionary[key] = $"Thread Id: {Thread.CurrentThread.ManagedThreadId}, number({key}): {random.Next(10)}"; + // dictionary.TryAdd(key, $"Thread Id: {Thread.CurrentThread.ManagedThreadId}, number({key}): {random.Next(10)}"); + // } + // } + // }); + // } + + Task[] tasks = new Task[100]; + Random random = new Random(); + File.Delete("output.txt"); + for (int i = 0; i < tasks.Length; i++) + { + tasks[i] = Task.Run(() => + { + // not thread-safe + // File.AppendAllText("output.txt", $"Thread Id: {Thread.CurrentThread.ManagedThreadId}, number: {random.Next(10)}\n"); + + // FileStream fs = File.OpenWrite("output.txt"); + // FileStream fs = File.Open("output.txt", FileMode.Append, FileAccess.Write, FileShare.ReadWrite); + // string s = $"{Thread.CurrentThread.ManagedThreadId}, number: {random.Next(10)}\n"; + // byte[] bytes = Encoding.UTF8.GetBytes(s); + // fs.Write(bytes, 0, bytes.Length); + // fs.Close(); + // fs.Dispose(); + + // thread-safe + lock (_fileLock) + { + FileStream fs = File.Open("output.txt", FileMode.Append, FileAccess.Write, FileShare.ReadWrite); + string s = $"{Thread.CurrentThread.ManagedThreadId}, number: {random.Next(10)}\n"; + byte[] bytes = Encoding.UTF8.GetBytes(s); + fs.Write(bytes, 0, bytes.Length); + fs.Close(); + fs.Dispose(); + } + }); + } + + Task.WaitAll(tasks); + + Console.WriteLine("Program ended"); + } + } +} \ No newline at end of file diff --git a/Multithreading/Tasks.cs b/Multithreading/Tasks.cs index e8ed82b..ea3a03c 100644 --- a/Multithreading/Tasks.cs +++ b/Multithreading/Tasks.cs @@ -1,83 +1,141 @@ -using System; -using System.Threading.Tasks; - -namespace Multithreading -{ - internal static class Program - { - public static void Main(string[] args) - { - Console.WriteLine("Program started"); - - // Task task = new Task(() => - // { - // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} started"); - // for (int i = 0; i < 10; i++) - // { - // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId}: {i}"); - // Thread.Sleep(500); - // } - // - // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} stopped"); - // }); - // task.Start(); - // - // - // Task task2 = Task.Run(() => - // { - // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} started"); - // for (int i = 0; i < 10; i++) - // { - // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId}: {i}"); - // Thread.Sleep(500); - // } - // - // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} stopped"); - // }); - // - // task.Wait(); - // task2.Wait(); - // - // Task task3 = new Task(() => - // { - // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} started"); - // for (int i = 0; i < 10; i++) - // { - // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId}: {i}"); - // Thread.Sleep(500); - // } - // - // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} stopped"); - // }); - // task3.RunSynchronously(); - - // Task[] tasks = new Task[10]; - // Random random = new Random(); - // for (int i = 0; i < 10; i++) - // { - // tasks[i] = Task.Run(() => - // { - // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} started"); - // for (int j = 0; j < 10; j++) - // { - // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId}: {j}"); - // Thread.Sleep(random.Next(100, 1000)); - // } - // - // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} stopped"); - // }); - // } - // - // // Task.WaitAll(tasks); - // Task.WaitAny(tasks); - - Parallel.For(0, 10000, i => - { - // do whatever you want to do in parallel - Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId}: {i}"); - }); - - Console.WriteLine("Program ended"); - } - } -} \ No newline at end of file +// using System; +// using System.Threading.Tasks; +// +// namespace Multithreading +// { +// internal static class Program +// { +// public static void Main(string[] args) +// { +// Console.WriteLine("Program started"); +// +// // Task task = new Task(() => +// // { +// // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} started"); +// // for (int i = 0; i < 10; i++) +// // { +// // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId}: {i}"); +// // Thread.Sleep(500); +// // } +// // +// // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} stopped"); +// // }); +// // task.Start(); +// // +// // +// // Task task2 = Task.Run(() => +// // { +// // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} started"); +// // for (int i = 0; i < 10; i++) +// // { +// // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId}: {i}"); +// // Thread.Sleep(500); +// // } +// // +// // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} stopped"); +// // }); +// // +// // task.Wait(); +// // task2.Wait(); +// // +// // Task task3 = new Task(() => +// // { +// // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} started"); +// // for (int i = 0; i < 10; i++) +// // { +// // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId}: {i}"); +// // Thread.Sleep(500); +// // } +// // +// // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} stopped"); +// // }); +// // task3.RunSynchronously(); +// +// // Task[] tasks = new Task[10]; +// // Random random = new Random(); +// // for (int i = 0; i < 10; i++) +// // { +// // tasks[i] = Task.Run(() => +// // { +// // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} started"); +// // for (int j = 0; j < 10; j++) +// // { +// // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId}: {j}"); +// // Thread.Sleep(random.Next(100, 1000)); +// // } +// // +// // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} stopped"); +// // }); +// // } +// // +// // // Task.WaitAll(tasks); +// // Task.WaitAny(tasks); +// +// // Parallel.For(0, 10000, i => +// // { +// // // do whatever you want to do in parallel +// // Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId}: {i}"); +// // }); +// +// CancellationTokenSource cts = new CancellationTokenSource(); +// CancellationToken token = cts.Token; +// +// Task task4 = Task.Run(() => +// { +// Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} started"); +// for (int i = 0; i < 100; i++) +// { +// Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId}: {i}"); +// Thread.Sleep(500); +// +// // token.ThrowIfCancellationRequested(); +// if (token.IsCancellationRequested) +// { +// // tidy up +// break; +// } +// } +// Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} stopped"); +// }, token); +// +// Task[] tasks2 = new Task[10]; +// for (int i = 0; i < tasks2.Length; i++) +// { +// tasks2[i] = Task.Run(() => +// { +// Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} started"); +// for (int i = 0; i < 100; i++) +// { +// Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId}: {i}"); +// Thread.Sleep(500); +// +// // token.ThrowIfCancellationRequested(); +// if (token.IsCancellationRequested) +// { +// // tidy up +// break; +// } +// } +// Console.WriteLine($"Task with Thread {Thread.CurrentThread.ManagedThreadId} stopped"); +// }); +// } +// +// if (Console.ReadKey().Key == ConsoleKey.A) +// { +// cts.Cancel(); +// cts.Dispose(); +// cts = null; +// } +// +// Thread.Sleep(1000); +// +// if (cts != null) +// { +// cts.Dispose(); +// cts = null; +// } +// +// Console.WriteLine("Program ended"); +// } +// } +// } \ No newline at end of file