├── .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 |
--------------------------------------------------------------------------------