├── .gitignore ├── AsyncTips.sln ├── AsyncTips ├── AsyncTips.csproj └── Program.cs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Autosave files 2 | *~ 3 | 4 | # build 5 | [Oo]bj/ 6 | [Bb]in/ 7 | packages/ 8 | TestResults/ 9 | 10 | # globs 11 | Makefile.in 12 | *.DS_Store 13 | *.sln.cache 14 | *.suo 15 | *.cache 16 | *.pidb 17 | *.userprefs 18 | *.usertasks 19 | config.log 20 | config.make 21 | config.status 22 | aclocal.m4 23 | install-sh 24 | autom4te.cache/ 25 | *.user 26 | *.tar.gz 27 | tarballs/ 28 | test-results/ 29 | Thumbs.db 30 | 31 | # Mac bundle stuff 32 | *.dmg 33 | *.app 34 | 35 | # resharper 36 | *_Resharper.* 37 | *.Resharper 38 | 39 | # dotCover 40 | *.dotCover 41 | 42 | *.db 43 | 44 | sources/.vs/EfficientApiCalls/xs/UserPrefs.xml 45 | -------------------------------------------------------------------------------- /AsyncTips.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsyncTips", "AsyncTips\AsyncTips.csproj", "{5ADC9AEF-862C-46EC-8C19-AB4C553FB6EB}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {5ADC9AEF-862C-46EC-8C19-AB4C553FB6EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {5ADC9AEF-862C-46EC-8C19-AB4C553FB6EB}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {5ADC9AEF-862C-46EC-8C19-AB4C553FB6EB}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {5ADC9AEF-862C-46EC-8C19-AB4C553FB6EB}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | EndGlobal 18 | -------------------------------------------------------------------------------- /AsyncTips/AsyncTips.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AsyncTips/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Threading; 4 | 5 | namespace AsyncTips 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | ExecuteTaskAsync().Wait(); 12 | 13 | ExecuteTaskWithTimeoutAsync(TimeSpan.FromSeconds(2)).Wait(); 14 | ExecuteTaskWithTimeoutAsync(TimeSpan.FromSeconds(6)).Wait(); 15 | 16 | ExecuteManuallyCancellableTaskAsync().Wait(); 17 | ExecuteManuallyCancellableTaskAsync().Wait(); 18 | 19 | CancelANonCancellableTaskAsync().Wait(); 20 | } 21 | 22 | /// 23 | /// Compute a value for a long time. 24 | /// 25 | /// The value computed. 26 | /// Number of iterations to do. 27 | private static Task LongRunningOperation(int loop) 28 | { 29 | // Start a task a return it 30 | return Task.Run(() => 31 | { 32 | decimal result = 0; 33 | 34 | // Loop for a defined number of iterations 35 | for (int i = 0; i < loop; i++) 36 | { 37 | // Do something that takes times like a Thread.Sleep in .NET Core 2. 38 | Thread.Sleep(10); 39 | result += i; 40 | } 41 | 42 | return result; 43 | }); 44 | } 45 | 46 | public static async Task ExecuteTaskAsync() 47 | { 48 | Console.WriteLine(nameof(ExecuteTaskAsync)); 49 | Console.WriteLine("Result {0}", await LongRunningOperation(100)); 50 | Console.WriteLine("Press enter to continue"); 51 | Console.ReadLine(); 52 | } 53 | 54 | /// 55 | /// Compute a value for a long time. 56 | /// 57 | /// The value computed. 58 | /// Number of iterations to do. 59 | /// The cancellation token. 60 | private static Task LongRunningCancellableOperation(int loop, CancellationToken cancellationToken) 61 | { 62 | Task task = null; 63 | 64 | // Start a task a return it 65 | task = Task.Run(() => 66 | { 67 | decimal result = 0; 68 | 69 | // Loop for a defined number of iterations 70 | for (int i = 0; i < loop; i++) 71 | { 72 | // Check if a cancellation is requested, if yes, 73 | // throw a TaskCanceledException. 74 | if (cancellationToken.IsCancellationRequested) 75 | throw new TaskCanceledException(task); 76 | 77 | cancellationToken.ThrowIfCancellationRequested(); 78 | 79 | // Do something that takes times like a Thread.Sleep in .NET Core 2. 80 | Thread.Sleep(10); 81 | result += i; 82 | } 83 | 84 | return result; 85 | }); 86 | 87 | return task; 88 | } 89 | 90 | public static async Task ExecuteTaskWithTimeoutAsync(TimeSpan timeSpan) 91 | { 92 | Console.WriteLine(nameof(ExecuteTaskWithTimeoutAsync)); 93 | 94 | using (var cancellationTokenSource = new CancellationTokenSource(timeSpan)) 95 | { 96 | try 97 | { 98 | var result = await LongRunningCancellableOperation(500, cancellationTokenSource.Token); 99 | Console.WriteLine("Result {0}", result); 100 | } 101 | catch (TaskCanceledException) 102 | { 103 | Console.WriteLine("Task was cancelled"); 104 | } 105 | } 106 | Console.WriteLine("Press enter to continue"); 107 | Console.ReadLine(); 108 | } 109 | 110 | public static async Task ExecuteManuallyCancellableTaskAsync() 111 | { 112 | Console.WriteLine(nameof(ExecuteManuallyCancellableTaskAsync)); 113 | 114 | using (var cancellationTokenSource = new CancellationTokenSource()) 115 | { 116 | // Creating a task to listen to keyboard key press 117 | var keyBoardTask = Task.Run(() => 118 | { 119 | Console.WriteLine("Press enter to cancel"); 120 | Console.ReadKey(); 121 | 122 | // Cancel the task 123 | cancellationTokenSource.Cancel(); 124 | }); 125 | 126 | try 127 | { 128 | var longRunningTask = LongRunningCancellableOperation(500, cancellationTokenSource.Token); 129 | 130 | var result = await longRunningTask; 131 | Console.WriteLine("Result {0}", result); 132 | Console.WriteLine("Press enter to continue"); 133 | } 134 | catch (TaskCanceledException) 135 | { 136 | Console.WriteLine("Task was cancelled"); 137 | } 138 | 139 | await keyBoardTask; 140 | } 141 | } 142 | 143 | 144 | private static async Task LongRunningOperationWithCancellationTokenAsync(int loop, CancellationToken cancellationToken) 145 | { 146 | // We create a TaskCompletionSource of decimal 147 | var taskCompletionSource = new TaskCompletionSource(); 148 | 149 | // Registering a lambda into the cancellationToken 150 | cancellationToken.Register(() => 151 | { 152 | // We received a cancellation message, cancel the TaskCompletionSource.Task 153 | taskCompletionSource.TrySetCanceled(); 154 | }); 155 | 156 | var task = LongRunningOperation(loop); 157 | 158 | // Wait for the first task to finish among the two 159 | var completedTask = await Task.WhenAny(task, taskCompletionSource.Task); 160 | 161 | // If the completed task is our long running operation we set its result. 162 | if (completedTask == task) 163 | { 164 | // Extract the result, the task is finished and the await will return immediately 165 | var result = await task; 166 | 167 | // Set the taskCompletionSource result 168 | taskCompletionSource.TrySetResult(result); 169 | } 170 | 171 | // Return the result of the TaskCompletionSource.Task 172 | return await taskCompletionSource.Task; 173 | } 174 | 175 | public static async Task CancelANonCancellableTaskAsync() 176 | { 177 | Console.WriteLine(nameof(CancelANonCancellableTaskAsync)); 178 | 179 | using (var cancellationTokenSource = new CancellationTokenSource()) 180 | { 181 | // Listening to key press to cancel 182 | var keyBoardTask = Task.Run(() => 183 | { 184 | Console.WriteLine("Press enter to cancel"); 185 | Console.ReadKey(); 186 | 187 | // Sending the cancellation message 188 | cancellationTokenSource.Cancel(); 189 | }); 190 | 191 | try 192 | { 193 | // Running the long running task 194 | var longRunningTask = LongRunningOperationWithCancellationTokenAsync(100, cancellationTokenSource.Token); 195 | var result = await longRunningTask; 196 | 197 | Console.WriteLine("Result {0}", result); 198 | Console.WriteLine("Press enter to continue"); 199 | } 200 | catch (TaskCanceledException) 201 | { 202 | Console.WriteLine("Task was cancelled"); 203 | } 204 | 205 | await keyBoardTask; 206 | } 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AsyncTips 2 | Various tips and tricks for async in C# 3 | 4 | Sample source code for the following blog post [https://johnthiriet.com/cancel-asynchronous-operation-in-csharp/](https://johnthiriet.com/cancel-asynchronous-operation-in-csharp/) 5 | --------------------------------------------------------------------------------