CancellationToken
-
private static async Task MainAsync() { using (var cts = new CancellationTokenSource(1000)) { var start = DateTime.Now; await Task.Run(() => Task.Delay(15000), cts.Token); var end = DateTime.Now; Console.WriteLine($"Waited {end - start}"); } }
Does this method:
- Throw an
OperationCancelledException
- Output a duration close to 1 second
- Output a duration close to 15 seconds
Answer: the stupidest possible choice from these options.
- Throw an
-
Relevant SO thread on the matter:
-
@ben_lubar Why
MainAsync
? Why not justMain
?
-
@pie_flavor ...because it is awaiting an async task? I mean, yeah you can call Result, but imo that is (usually) bad practice in modern C#
-
@ben_lubar - You really need to review the very basics....
-
@thecpuwizard said in CancellationToken:
@ben_lubar - You really need to review the very basics....
So, when I pass a cancellation token into a method, it's supposed to completely ignore it and that's expected behavior?
-
@ben_lubar said in CancellationToken:
@thecpuwizard said in CancellationToken:
@ben_lubar - You really need to review the very basics....
So, when I pass a cancellation token into a method, it's supposed to completely ignore it and that's expected behavior?
The schedule checks the token before starting the task. Once the task is running it is your (the person implementing the code inside the task) responsibility to monitor it and take action.
-
@the_quiet_one said in CancellationToken:
@pie_flavor ...because it is awaiting an async task? I mean, yeah you can call Result, but imo that is (usually) bad practice in modern C#
???
private static async Task Main() {
-
@thecpuwizard said in CancellationToken:
Once the task is running it is your (the person implementing the code inside the task) responsibility to monitor it and take action.
Which I can obtain from... where, exactly?
-
@pie_flavor said in CancellationToken:
@the_quiet_one said in CancellationToken:
@pie_flavor ...because it is awaiting an async task? I mean, yeah you can call Result, but imo that is (usually) bad practice in modern C#
???
private static async Task Main() {
Because Main is already defined:
using System; using System.Threading; using System.Threading.Tasks; public class Program { public static void Main() { MainAsync().Wait(); } private static async Task MainAsync() { using (var cts = new CancellationTokenSource(1000)) { var start = DateTime.Now; await Task.Run(() => Task.Delay(15000), cts.Token); var end = DateTime.Now; Console.WriteLine($"Waited {end - start}"); } } }
-
@ben_lubar my money is on the exception.
Edit: apparently I would have lost coin.
-
@ben_lubar said in CancellationToken:
@thecpuwizard said in CancellationToken:
Once the task is running it is your (the person implementing the code inside the task) responsibility to monitor it and take action.
Which I can obtain from... where, exactly?
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;Task myTask = Task.Factory.StartNew(() =>
{
for (...)
{
token.ThrowIfCancellationRequested();
}
}, token);
-
@thecpuwizard said in CancellationToken:
@ben_lubar said in CancellationToken:
@thecpuwizard said in CancellationToken:
Once the task is running it is your (the person implementing the code inside the task) responsibility to monitor it and take action.
Which I can obtain from... where, exactly?
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;Task myTask = Task.Factory.StartNew(() =>
{
for (...)
{
token.ThrowIfCancellationRequested();
}
}, token);And the token I passed into
Task.Factory.StartNew
is accessible from?
-
@ben_lubar said in CancellationToken:
@pie_flavor said in CancellationToken:
@the_quiet_one said in CancellationToken:
@pie_flavor ...because it is awaiting an async task? I mean, yeah you can call Result, but imo that is (usually) bad practice in modern C#
???
private static async Task Main() {
Because Main is already defined:
using System; using System.Threading; using System.Threading.Tasks; public class Program { public static void Main() { MainAsync().Wait(); } private static async Task MainAsync() { using (var cts = new CancellationTokenSource(1000)) { var start = DateTime.Now; await Task.Run(() => Task.Delay(15000), cts.Token); var end = DateTime.Now; Console.WriteLine($"Waited {end - start}"); } } }
using System; using System.Threading; using System.Threading.Tasks; internal class Program { private static async Task Main() { using (var cts = new CancellationTokenSource(1000)) { var start = DateTime.Now; await Task.Run(() => Task.Delay(15000), cts.Token); var end = DateTime.Now; Console.WriteLine($"Waited {end - start}"); } } }
-
@pie_flavor and that private method that does not return void gets called from... where, exactly?
-
@ben_lubar said in CancellationToken:
So, when I pass a cancellation token into a method, it's supposed to completely ignore it and that's expected behavior?
You're supposed to check it yourself, something like
while( ! CancellationPending ) { DoStuff(); }
. I don't think anything in the framework does that for you if you don't.
-
@blakeyrat said in CancellationToken:
@ben_lubar said in CancellationToken:
So, when I pass a cancellation token into a method, it's supposed to completely ignore it and that's expected behavior?
You're supposed to check it yourself, something like
while( ! CancellationPending ) { DoStuff(); }
. I don't think anything in the framework does that for you if you don't.Ok, but if the cancellation token I give to the task constructor is completely ignored, why should it have a parameter for that at all?
-
@ben_lubar said in CancellationToken:
Ok, but if the cancellation token I give to the task constructor is completely ignored,
Why do you think this is the case?
-
@blakeyrat because it doesn't work?
-
@blakeyrat said in CancellationToken:
@ben_lubar said in CancellationToken:
Ok, but if the cancellation token I give to the task constructor is completely ignored,
Why do you think this is the case?
If the cancellation token I gave it was used, the await on the task would be cancelled by the token.
-
@gÄ…ska Sigh.
The answer's in the SO page Quiet_One linked to.
If you pass a CancellationToken in the constructor, C# can use it to decide not to run the task at all, if that token has "fired" before the task comes up in the schedule.
If you don't care about that little optimization, then don't bother passing the token in and just check it on the first line of your task method.
-
@blakeyrat said in CancellationToken:
@gÄ…ska Sigh.
The answer's in the SO page Quiet_One linked to.
If you pass a CancellationToken in the constructor, C# can use it to decide not to run the task at all, if that token has "fired" before the task comes up in the schedule.
Here's the documentation:
// Summary: // Queues the specified work to run on the thread pool and returns a proxy for the // task returned by function. // // Parameters: // function: // The work to execute asynchronously. // // cancellationToken: // A cancellation token that should be used to cancel the work. // // Returns: // A task that represents a proxy for the task returned by function. // // Exceptions: // T:System.ArgumentNullException: // The function parameter was null. // // T:System.Threading.Tasks.TaskCanceledException: // The task has been canceled. // // T:System.ObjectDisposedException: // The System.Threading.CancellationTokenSource associated with cancellationToken // was disposed.
Assuming that the documentation is correct, what do you expect this code to do?
using System; using System.Threading; using System.Threading.Tasks; public class Program { public static void Main() { MainAsync().Wait(); } private static async Task MainAsync() { using (var cts = new CancellationTokenSource(1000)) { var start = DateTime.Now; var task = Task.Run(() => { Thread.Sleep(5000); return Task.Delay(10000); }, cts.Token); await Task.Delay(2000); await task; var end = DateTime.Now; Console.WriteLine($"Waited {end - start}"); } } }
-
@ben_lubar I expect it to output constant fart jokes.
-
@blakeyrat said in CancellationToken:
@ben_lubar I expect it to output constant fart jokes.
Wow, the documentation is worse than I thought!
-
@ben_lubar said in CancellationToken:
private static async Task MainAsync()
{
using (var cts = new CancellationTokenSource(1000))
{
var start = DateTime.Now;
var task = Task.Run(() =>
{
Thread.Sleep(5000);
return Task.Delay(10000);
}, cts.Token);
await Task.Delay(2000);
await task;
var end = DateTime.Now;Console.WriteLine($"Waited {end - start}"); } }
Wait for 15 seconds (plus overhead, of course)
-
@ben_lubar The 'work' is not individual components of the function. The 'work' is the entire function. If the cancellation token cancels before the task actually gets run, it won't run the task. Anything else would result in some extremely weird behavior. What you should do instead is pass the CancellationToken to a sub unit of work (i.e. the Task.Delay method). Since this is a closure, you can just use the token; otherwise, you would be passing it as a method parameter.
tl;dr The framework does not micromanage your code for you.private static async Task Main() { using (var cts = new CancellationTokenSource(1000)) { var token = cts.Token; var start = DateTime.Now; await Task.Run(() => Task.Delay(10000, token), token); var end = DateTime.Now; Console.WriteLine($"Waited {end - start}"); } }
This will net you the OperationCancelledException you were expecting.
-
@ben_lubar Have you tried passing the token to Task.Delay as well seeing as how it has an overload that takes one?
-
@ben_lubar I predict that it'll be a couple of milliseconds, because the token will already be cancelled by the time the task runs (also why do you keep using both
Main()
andMainAsync()
instead of just having a single asyncMain()
?)
Edit: Wait, no, the task already got run when you constructed it. So same behavior as before. Theawait
doesn't actually run it.
-
@powerlord said in CancellationToken:
@ben_lubar Have you tried passing the token to Task.Delay as well seeing as how it has an overload that takes one?
Of course he hasn't
-
@thecpuwizard said in CancellationToken:
@powerlord said in CancellationToken:
@ben_lubar Have you tried passing the token to Task.Delay as well seeing as how it has an overload that takes one?
Of course he hasn't
Yeah, I'll just pass the cancellation token to the overload of
Task.Delay
that runs processes over SSH.
-
@pie_flavor said in CancellationToken:
Edit: Wait, no, the task already got run when you constructed it.
WHY DOES
Task.Run
TAKE AN ARGUMENT THAT TELLS IT IF IT SHOULD START TO RUN THE TASK
-
@ben_lubar said in CancellationToken:
@pie_flavor said in CancellationToken:
Edit: Wait, no, the task already got run when you constructed it.
WHY DOES
Task.Run
TAKE AN ARGUMENT THAT TELLS IT IF IT SHOULD START TO RUN THE TASKBecause it is queued. You can call Task.Run() and it be hours before the task actually starts (unusual but possible).
-
@ben_lubar said in CancellationToken:
@thecpuwizard said in CancellationToken:
@powerlord said in CancellationToken:
@ben_lubar Have you tried passing the token to Task.Delay as well seeing as how it has an overload that takes one?
Of course he hasn't
Yeah, I'll just pass the cancellation token to the overload of
Task.Delay
that runs processes over SSH.You may want to look at what a CalcelationToken actually is:
-
@thecpuwizard said in CancellationToken:
@ben_lubar said in CancellationToken:
@thecpuwizard said in CancellationToken:
@powerlord said in CancellationToken:
@ben_lubar Have you tried passing the token to Task.Delay as well seeing as how it has an overload that takes one?
Of course he hasn't
Yeah, I'll just pass the cancellation token to the overload of
Task.Delay
that runs processes over SSH.You may want to look at what a CalcelationToken actually is:
using System; using System.Threading; using System.Threading.Tasks; public class Program { public static void Main() { MainAsync().Wait(); } private static async Task MainAsync() { using (var cts = new CancellationTokenSource(1000)) { Console.WriteLine(await WithCancel(async () => { await Task.Delay(15000); return 123; }, cts.Token)); } } public static async Task<T> WithCancel<T>(Func<Task<T>> work, CancellationToken cancellationToken) { var tcs = new TaskCompletionSource<T>(); using (cancellationToken.Register(() => tcs.TrySetCanceled(cancellationToken))) { return await await Task.WhenAny(work(), tcs.Task); } } }
Look ma, I wrote a function that lets me cancel awaiting a task that actually works unlike
Task.Run
-
@ben_lubar said in CancellationToken:
@thecpuwizard said in CancellationToken:
@ben_lubar said in CancellationToken:
@thecpuwizard said in CancellationToken:
@powerlord said in CancellationToken:
@ben_lubar Have you tried passing the token to Task.Delay as well seeing as how it has an overload that takes one?
Of course he hasn't
Why Not?
using System; using System.Threading; using System.Threading.Tasks; public class Program { public static void Main() { MainAsync().Wait(); } private static async Task MainAsync() { using (var cts = new CancellationTokenSource(1000)) { Console.WriteLine(await WithCancel(async () => { await Task.Delay(15000, cts.Token); return 123; }, cts.Token)); } }
-
@ben_lubar said in CancellationToken:
Look ma, I wrote a function that lets me cancel awaiting a task that actually works unlike
Task.Run
FYI: Yours does not really "work".... Consider what happens if you had
await Task.Delay(15000); shell("FORMAT C:"); return 123;
Ant the Task was canceled during the delay time...
-
@thecpuwizard said in CancellationToken:
@ben_lubar said in CancellationToken:
Look ma, I wrote a function that lets me cancel awaiting a task that actually works unlike
Task.Run
FYI: Yours does not really "work".... Consider what happens if you had
await Task.Delay(15000); shell("FORMAT C:"); return 123;
Ant the Task was canceled during the delay time...
Except that all I care about cancelling is the wait. The actual shell command gets killed as soon as the IRemoteProcess is disposed: https://github.com/Inedo/inedox-linux/blob/e0d40c36ebd32bb757eb8941c07890918ab75b40/Linux/Common/Operations/SHUtil.cs#L73-L80
IRemoteProcessExecuter.CreateProcess
doesn't know anything aboutcontext.CancellationToken
.
-
@ben_lubar said in CancellationToken:
@thecpuwizard said in CancellationToken:
@ben_lubar said in CancellationToken:
Look ma, I wrote a function that lets me cancel awaiting a task that actually works unlike
Task.Run
FYI: Yours does not really "work".... Consider what happens if you had
await Task.Delay(15000); shell("FORMAT C:"); return 123;
Ant the Task was canceled during the delay time...
Except that all I care about cancelling is the wait. The actual shell command gets killed as soon as the IRemoteProcess is disposed: https://github.com/Inedo/inedox-linux/blob/e0d40c36ebd32bb757eb8941c07890918ab75b40/Linux/Common/Operations/SHUtil.cs#L73-L80
IRemoteProcessExecuter.CreateProcess
doesn't know anything aboutcontext.CancellationToken
.I would have to see what the implementation of WaitAsyncis...
using (var process = ps.CreateProcess(new RemoteProcessStartInfo { FileName = fileName, WorkingDirectory = context.WorkingDirectory, Arguments = arguments })) { process.OutputDataReceived += (s, e) => output(e.Data); process.ErrorDataReceived += (s, e) => LogMessage(errorLevel, e.Data, logger); process.Start(); await process.WaitAsync(context.CancellationToken).ConfigureAwait(false); exitCode = process.ExitCode; }
-
@thecpuwizard It's literally just an async wait.