CancellationTokenSource.Cancel() hangs - c#

I'm observing hang in CancellationTokenSource.Cancel when one of the async is in an active loop.
Full code:
static async Task doStuff(CancellationToken token)
{
try
{
// await Task.Yield();
await Task.Delay(-1, token);
}
catch (TaskCanceledException)
{
}
while (true) ;
}
static void Main(string[] args)
{
var main = Task.Run(() =>
{
using (var csource = new CancellationTokenSource())
{
var task = doStuff(csource.Token);
Console.WriteLine("Spawned");
csource.Cancel();
Console.WriteLine("Cancelled");
}
});
main.GetAwaiter().GetResult();
}
Prints Spawned and hangs. Callstack looks like:
ConsoleApp9.exe!ConsoleApp9.Program.doStuff(System.Threading.CancellationToken token) Line 23 C#
[Resuming Async Method]
[External Code]
ConsoleApp9.exe!ConsoleApp9.Program.Main.AnonymousMethod__1_0() Line 34 C#
[External Code]
Uncommeting await Task.Yield would result in Spawned\nCancelled in output.
Any ideas why? Does C# guarantee that once-yielded async would never block other asyncs?

CancellationTokenSource does not have any notion of task scheduler. If the callback wasn't registered with a custom synchronization context, CancellationTokenSource will execute it in the same callstack as .Cancel(). In your case, the cancellation callback completes the task returned by Task.Delay, then the continuation is inlined, resulting in an infinite loop inside of CancellationTokenSource.Cancel.
Your example with Task.Yield only works because of a race condition. When the token is cancelled, the thread hasn't started executing Task.Delay, therefore there is no continuation to inline. If you change your Main to add a pause, you'll see that it'll still freeze even with Task.Yield:
static void Main(string[] args)
{
var main = Task.Run(() =>
{
using (var csource = new CancellationTokenSource())
{
var task = doStuff(csource.Token);
Console.WriteLine("Spawned");
Thread.Sleep(1000); // Give enough time to reach Task.Delay
csource.Cancel();
Console.WriteLine("Cancelled");
}
});
main.GetAwaiter().GetResult();
}
Right now, the only way reliable to protect a call to CancellationTokenSource.Cancel is to wrap it in Task.Run.

Related

Switch new Task(()=>{ }) for Func<Task>

In an answer to one of my other questions, I was told that use of new Task(() => { }) is not something that is a normal use case. I was advised to use Func<Task> instead. I have tried to make that work, but I can't seem to figure it out. (Rather than drag it out in the comments, I am asking a separate question here.)
My specific scenario is that I need the Task to not start right when it is declared and to be able to wait for it later.
Here is a LinqPad example using new Task(() => { }). NOTE: This works perfectly! (Except that it uses new Task.)
static async void Main(string[] args)
{
// Line that I need to swap to a Func<Task> somehow.
// note that this is "cold" not started task
Task startupDone = new Task(() => { });
var runTask = DoStuff(() =>
{
//+++ This is where we want to task to "start"
startupDone.Start();
});
//+++ Here we wait for the task to possibly start and finish. Or timeout.
// Note that this times out at 1000ms even if "blocking = 10000" below.
var didStartup = startupDone.Wait(1000);
Console.WriteLine(!didStartup ? "Startup Timed Out" : "Startup Finished");
await runTask;
Console.Read();
}
public static async Task DoStuff(Action action)
{
// Swap to 1000 to simulate starting up blocking
var blocking = 1; //1000;
await Task.Delay(500 + blocking);
action();
// Do the rest of the stuff...
await Task.Delay(1000);
}
I tried swapping the second line with:
Func<Task> startupDone = new Func<Task>(async () => { });
But then the lines below the comments with +++ in them don't work right.
I swapped the startupDone.Start() with startupDone.Invoke().
But startupDone.Wait needs the task. Which is only returned in the lambda. I am not sure how to get access to the task outside the lambda so I can Wait for it.
How can use a Func<Task> and start it in one part of my code and do a Wait for it in another part of my code? (Like I can with new Task(() => { })).
The code you posted cannot be refactored to make use of a Func<Task> instead of a cold task, because the method that needs to await the task (the Main method) is not the same method that controls the creation/starting of the task (the lambda parameter of the DoStuff method). This could make the use of the Task constructor legitimate in this case, depending on whether the design decision to delegate the starting of the task to a lambda is justified. In this particular example the startupDone is used as a synchronization primitive, to signal that a condition has been met and the program can continue. This could be achieved equally well by using a specialized synchronization primitive, like for example a SemaphoreSlim:
static async Task Main(string[] args)
{
var startupSemaphore = new SemaphoreSlim(0);
Task runTask = RunAsync(startupSemaphore);
bool startupFinished = await startupSemaphore.WaitAsync(1000);
Console.WriteLine(startupFinished ? "Startup Finished" : "Startup Timed Out");
await runTask;
}
public static async Task RunAsync(SemaphoreSlim startupSemaphore)
{
await Task.Delay(500);
startupSemaphore.Release(); // Signal that the startup is done
await Task.Delay(1000);
}
In my opinion using a SemaphoreSlim is more meaningful in this case, and makes the intent of the code clearer. It also allows to await asynchronously the signal with a timeout WaitAsync(Int32), which is not something that you get from a Task out of the box (it is doable though).
Using cold tasks may be tempting in some cases, but when you revisit your code after a month or two you'll find yourself confused, because of how rare and unexpected is to have to deal with tasks that may or may have not been started yet.
I always try my hardest to never have blocking behavior when dealing with anything async or any type that represents potential async behavior such as Task. You can slightly modify your DoStuff to facilitate waiting on your Action.
static async void Main(string[] args)
{
Func<CancellationToken,Task> startupTask = async(token)=>
{
Console.WriteLine("Waiting");
await Task.Delay(3000, token);
Console.WriteLine("Completed");
};
using var source = new CancellationTokenSource(2000);
var runTask = DoStuff(() => startupTask(source.Token), source.Token);
var didStartup = await runTask;
Console.WriteLine(!didStartup ? "Startup Timed Out" : "Startup Finished");
Console.Read();
}
public static async Task<bool> DoStuff(Func<Task> action, CancellationToken token)
{
var blocking = 10000;
try
{
await Task.Delay(500 + blocking, token);
await action();
}
catch(TaskCanceledException ex)
{
return false;
}
await Task.Delay(1000);
return true;
}
First, the type of your "do this later" object is going to become Func<Task>. Then, when the task is started (by invoking the function), you get back a Task that represents the operation:
static async void Main(string[] args)
{
Func<Task> startupDoneDelegate = async () => { };
Task startupDoneTask = null;
var runTask = await DoStuff(() =>
{
startupDoneTask = startupDoneDelegate();
});
var didStartup = startupDoneTask.Wait(1000);
Console.WriteLine(!didStartup ? "Startup Timed Out" : "Startup Finished");
}

Why await sometimes create new thread but sometimes not?

class Program
{
static void Main(string[] args)
{
var rst = DownloadPage("http://www.baidu.com");
//var rst2=GetString();
Console.ReadKey();
}
private static async Task<string> DownloadPage(string url)
{
using (var client = new HttpClient())
{
PringMsgWithThreadId("Before await");
var response = await client.GetAsync(url).ConfigureAwait(continueOnCapturedContext:false);
var content= await response.Content.ReadAsStringAsync();
PringMsgWithThreadId(content.Substring(0, 10));
PringMsgWithThreadId("After await");
return content;
}
}
private static async Task<string> GetString()
{
PringMsgWithThreadId("Before await");
var result = await GetStringAsync();
PringMsgWithThreadId(result);
PringMsgWithThreadId("After await");
return result;
}
private static Task<string> GetStringAsync()
{
var task = new Task<string>(() =>
{
Thread.Sleep(1000 * 2);
return "string after sleep two seconds";
});
task.RunSynchronously();
return task;
}
private static void PringMsgWithThreadId(string tag)
{
Console.WriteLine($"{tag}(ThreadId:{Thread.CurrentThread.ManagedThreadId})");
}
}
output when run DownloadPage() method output:
output when run GetString() method
My question:
1.when call DownloadPage(),why code after await executed in the thread(ThreadID:15) other than main thread(ThreadId:10).
2.when call GetString(),why code after await executed in the same thread(both threadId is 10)。
await never creates a new thread.
As explained on my async intro, await will first examine its argument (the task). If it has already completed, then it continues executing synchronously. Otherwise, it "pauses" the method and registers a callback with its argument (i.e., places a continuation on the task).
Later, when the task completes, the continuation will run. Since you're in a Console app without a SynchronizationContext/TaskScheduler, that continuation will run on a thread pool thread.
So, the answer to your first question is that the main thread is busy (blocked in Console.ReadKey), and also the main thread in a Console app isn't a thread pool thread anyway. The answer to your second question is because the task in GetStringAsync is run synchronously and is already complete by the time it returns, and this causes the await in GetString to continue (synchronously).
On a side note, you should never, ever, use the task constructor. If you want to return an already-completed task, use Task.FromResult. If you want to execute some work on a background thread, use Task.Run.

Why is this Task not Finishing before my code passes the Wait Command

I have a task which runs a loop and delays for an interval each iteration. Once the CancellationTokenSource calls Cancel() I want my main code to Wait() for the Task.Delay(interval) to finish and the task to exit the loop before my code continues. I thought this code would work but it doesn't.
Instead my main code passes the t.Wait() before the Loop exits. Why?
Main Method Code:
var cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
var t = Task.Run(() => { MyLoopTask(200, ct); });
// Prepare information
cts.Cancel();
t.Wait();
// Send Information
Task Code
private async Task MyLoopTask(int interval, CancellationToken cancelToken)
{
while (!cancelToken.IsCancellationRequested)
{
Debug.Print(" Still In Loop ");
// Do something
await Task.Delay(interval);
}
Debug.Print(" cancelled ");
//Clean up
}
You create a task with Task.Run that fires and forgets the actual task you get back from MyLoopTask.
Task.Run is redundant here. You can just call MyLoopTask and use the task it returns.
var t = MyLoopTask(200, ct);
// ...
t.Wait();
If you still have some reason to use Task.Run you can do so by making sure the delegate waits for the actual task by awaiting it:
var t = Task.Run(async () => await MyLoopTask(200, ct));
// ...
t.Wait();

NotOnRanToCompletion Continuation doesn't run when parent task is cancelled

I'm trying to test a scenario where I have a task that can be cancelled, and a continuation that should be running if the antecedent task doesn't complete. A sample of the code is like this:
static void Main(string[] args)
{
var source = new CancellationTokenSource();
var task = Task.Factory.StartNew(() =>
{
while (true)
{
source.Token.ThrowIfCancellationRequested();
}
}, source.Token);
var continuation = task.ContinueWith(t =>
{
Console.WriteLine("Continuation");
if (t.Status == TaskStatus.Faulted)
{
Console.WriteLine("Antecedent Faulted: " + t.Exception.Message);
}
}, source.Token, TaskContinuationOptions.NotOnRanToCompletion | TaskContinuationOptions.AttachedToParent, TaskScheduler.Current);
var cancellation = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
source.Cancel();
});
try
{
continuation.Wait();
}
catch (AggregateException)
{
Console.WriteLine("AggregateException");
}
Console.WriteLine("Done");
while (!Console.KeyAvailable) { }
}
The output of this program is:
AggregateException
Done
To a certain extent, I get it. The primary task was cancelled, which ends up throwing a TaskCanceledException which gets wrapped in the AggregateException. The question is, is this expected behaviour? If so, what use is TaskStatus.Faulted in a continuation, if that continuation isn't getting executed? I even set a looping check for ConsoleKeyAvailable just in case the continuation does get run as well as the AggregateException getting thrown.
I believe the reason this is occurring is you are passing source.Token as the CancellationToken for the call to task.ContinueWith. Despite passing NotOnRanToCompletion as the continuation options, the fact that the token is cancelled before the continuation task ever starts means the scheduler can immediately transition it to the canceled state without actually running it.

Why awaiting cold Task does not throw

I was just experimenting to see what happens when a cold task (i.e. a Task which hasn't been started) is awaited. To my surprise the code just hung forever and "Finsihed" is never printed. I would expect that an exception is thrown.
public async Task Test1()
{
var task = new Task(() => Thread.Sleep(1000));
//task.Start();
await task;
}
void Main()
{
Test1().Wait();
Console.WriteLine("Finished");
}
Then I though perhaps the task can be started from another thread, so I changed the code to:
public async Task Test1()
{
var task = new Task(() => Thread.Sleep(1000));
//task.Start();
await task;
Console.WriteLine("Test1 Finished");
}
void Main()
{
var task1 = Test1();
Task.Run(() =>
{
Task.Delay(5000);
task1.Start();
});
task1.Wait();
Console.WriteLine("Finished");
}
But it is still blocked at task1.Wait(). Does anyone know if there is way to start a cold task after it has being awaited?
Otherwise it seems there is no point in being able to await a cold task, so perhaps the task should either be started when awaited or an exception should be thrown.
Update
I was awaiting the wrong task, i.e. the outer task returned by Test1 rather than the one newed inside it. The InvalidOperationException mentioned by #Jon Skeet was being thrown inside Task.Run however because the resulting task was not observed, the exception was not thrown on the main thread. Putting a try/catch inside Task.Run or calling Wait() or Result on the task returned by Task.Run threw the exception on the main console thread.
You're trying to start the task returned by the async method - that isn't the cold task that you started out with. Indeed, if you add some diagnostics to your Task.Run call, you'll see that an exception is thrown:
System.InvalidOperationException: Start may not be called on a promise-style task.
Here's an example showing what I think you were really trying to do:
using System;
using System.Threading;
using System.Threading.Tasks;
public class Test
{
static void Main(string[] args)
{
// Not using Task.Delay! That would be pointless
Task t1 = new Task(() => Thread.Sleep(1000));
Task t2 = Await(t1);
Console.WriteLine(t2.Status);
Console.WriteLine("Starting original task");
t1.Start();
Console.WriteLine(t2.Status);
t2.Wait();
Console.WriteLine(t2.Status);
}
static async Task Await(Task task)
{
Console.WriteLine("Beginning awaiting");
await task;
Console.WriteLine("Finished awaiting");
}
}
Note the use of Thread.Sleep instead of Task.Delay; unless you're using the result of Task.Delay, it basically does nothing. Using Thread.Sleep is emulating real work to be done in the other task.
As for why awaiting an unstarted task doesn't throw an exception - I think that's reasonable, to be honest. It allows for situations like the above to be valid, which may in some cases make life easier. (You may create a lot of tasks before starting them, for example - and you may want to start waiting for them to finish before you start them.)
Does anyone know if there is way to start a cold task after it has
being awaited?
You still can create a cold task from an async method and start it later, if that's what you want:
class Program
{
public static async Task Test1()
{
await Task.Delay(1000);
Console.WriteLine("Test1 is about to finish");
}
static void Main(string[] args)
{
var taskOuter = new Task<Task>(Test1);
var taskInner = taskOuter.Unwrap();
Task.Run(() =>
{
Thread.Sleep(2000);
// run synchronously
taskOuter.RunSynchronously();
// or schedule
// taskOuter.Start(TaskScheduler.Defaut);
});
taskInner.Wait();
Console.WriteLine("Enter to exit");
Console.ReadLine();
}
}

Categories

Resources