I have a simple Console application with 2 running threads.
The first thread is measuring some values, and the second thread looks for user input and performs some mouse movements.
while (true)
{
if (Input.IsKeyDown(VC_L))
{
Mouse.Move(300, 500);
Thread.Sleep(thread1_delay);
Mouse.Move(670, 300);
Thread.Sleep(thread1_delay);
Mouse.Move(870, 700);
Thread.Sleep(thread1_delay);
}
}
The problem is I want to stop the second thread as soon as I get another key as input. But it's not working since the thread is still sleeping and doesn't react.
Just use a CancellationToken and be done with it
Propagates notification that operations should be canceled.
Example
public static async Task DoFunkyStuff(CancellationToken token)
{
// a logical escape for the loop
while (!token.IsCancellationRequested)
{
try
{
Console.WriteLine("Waiting");
await Task.Delay(1000, token);
}
catch (OperationCanceledException e)
{
Console.WriteLine("Task Cancelled");
}
}
Console.WriteLine("Finished");
}
Usage
static async Task Main(string[] args)
{
var ts = new CancellationTokenSource();
Console.WriteLine("Press key to cancel tasks");
var task = DoFunkyStuff(ts.Token);
// user input
Console.ReadKey();
Console.WriteLine("Cancelling token");
// this is how to cancel
ts.Cancel();
// just to prove the task has been cancelled
await task;
// because i can
Console.WriteLine("Elvis has left the building");
Console.ReadKey();
}
Results
Press key to cancel tasks
Waiting
Waiting
Waiting
Waiting
Waiting
Cancelling token
Task Cancelled
Finished
Elvis has left the building
The second thread should check for a Boolean value when it wakes up. You should set this value to false when your condition is met. Now when the second thread wakes up it will complete it's execution.
Related
I'm trying to use cancellation tokens to break out of a loop from another thread:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace CancellationTokenTest
{
class Program
{
private static CancellationTokenSource token;
static async Task Main(string[] args)
{
token = new CancellationTokenSource();
var t1 = LongProcess1();
for (int i = 0; i < 5; i++)
{
try
{
await Task.Delay(2000, token.Token);
Console.WriteLine($"Main awaited {i}");
}
catch (OperationCanceledException)
{
break;
}
}
Console.WriteLine("Main loop completed");
t1.Wait();
Console.WriteLine("Application end");
}
static async Task LongProcess1()
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(1000);
Console.WriteLine($"LongProcess1 awaited {i}");
}
Console.WriteLine("Submitting cancel");
token.Cancel(); // <------ Blocking execution
Console.WriteLine("Cancellation done!");
}
}
}
The problem is that the line Console.WriteLine("Cancellation done!"); never gets executed, and the Main() method keeps waiting for t1 to complete.
I do not understand why!
I ran this on .NET 5, but it doesn't seem to be framework dependent.
Can someone help me understand what's going on?
This deadlock is almost exactly like the one described on my blog. One thing you need to be aware of is that CancellationTokenSource.Cancel in this case is running the cancellation token continuations on the thread that calls Cancel.
So, here's what's happening:
Main starts LongProcess.
LongProcess uses some awaits and resumes on a thread pool thread.
In the meantime, Main is doing an await Task.Delay.
When LongProcess does the Cancel, it actually resumes executing the Main method on the current thread. You can verify this by placing a breakpoint in your catch block and looking at the call stack.
The current thread (a thread pool thread) then blocks on the task (.Wait()).
Now, LongProcess cannot complete because the thread currently running it is blocked waiting for it to complete.
To fix it, change the Wait() to an await.
The problem is the t1.Wait(); (Wait() is almost always the problem ;p) - this is stealing the thread. Use await t1; instead, and everything will work. Effectively, the cancellation needs to invoke some callbacks (the continuations in the cancellation state) and then return to whatever did the cancellation, and you've inserted a blockage into the cancellation.
I have an method in which I am using Task.Delay for 1 minute. So when I want to try cancel that Task then it's giving me error like system.threading.tasks.taskcanceledexception a task was canceled instead of cancel that Task.
So how can I cancel that Task with handle this error.
public static System.Threading.CancellationTokenSource tokenSource = new System.Threading.CancellationTokenSource();
tokenSource.cancel();
public static async void waitForSignal(System.Threading.CancellationToken token)
{
await Task.Delay(60000, token); //here I am getting error while I am defining tokenSource cancel.
}
So how can I cancel that Task with handle this error.
You're already canceling the task; you just need to handle the error:
try
{
await Task.Delay(60000, token);
}
catch (OperationCanceledException)
{
}
...
Actually admin is getting signals from wcf service in which if admin will not get expected signals from wcf then it will wait for 60 seconds and if admin will get expected numbers of signals in first 10 seconds then it will not required to wait for next 50 seconds.
Sounds like what you want is a signal, not a cancellation. You want to (asynchronously) wait for either a signal or a time period (delay), after which you want to take some action. You don't really want cancellation here.
One kind of asynchronous signal is TaskCompletionSource<T>. Your action code can await the Task of that TCS, and the signaling code can call SetResult to send the signal. Something like this:
public static TaskCompletionSource<object> signal = new TaskCompletionSource<object>();
...
signal.SetResult(null);
...
public static async Task waitForSignal(Task signalTask)
{
await Task.WhenAny(Task.Delay(60000), signalTask);
}
I am calling some code in Parallel.Foreach(). The code has Thread.Sleep(60000), so if I cancel the token also then it waits for 60 seconds before cancelling the Parallel.ForEach loop.
Thread.Sleep() is put in this code for explanation. The actual code has some code that waits for other resources.
I want to cancel all activity as cts.Cancel(); is called.
I tried LoopState also but it will not work in my case.
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main()
{
int[] nums = Enumerable.Range(0, 10000000).ToArray();
CancellationTokenSource cts = new CancellationTokenSource();
// Use ParallelOptions instance to store the CancellationToken
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;
po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
Console.WriteLine("Press any key to start. Press 'c' to cancel.");
Console.ReadKey();
// Run a task so that we can cancel from another thread.
Task.Factory.StartNew(() =>
{
if (Console.ReadKey().KeyChar == 'c')
{
cts.Cancel();
}
Console.WriteLine("press any key to exit");
});
try
{
Parallel.ForEach(nums, po, (num) =>
{
double d = Math.Sqrt(num);
Console.WriteLine("{0} on {1}", d, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(60000); //Thread Sleep for 1 minute
po.CancellationToken.ThrowIfCancellationRequested();
});
}
catch (OperationCanceledException e)
{
Console.WriteLine(e.Message);
}
finally
{
cts.Dispose();
}
Console.ReadKey();
}
}
If I understand correctly, it is not really that you need to interrupt the thread immediately, but rather that you simply want to be able to interrupt the Sleep() method while it's waiting.
There are a number of options, but one of the simplest IMHO is to use the CancellationToken.WaitHandle property value, and wait on that instead of sleeping:
po.CancellationToken.WaitHandle.WaitOne(60000);
If the token is signaled, the Wait() method will return before the timeout specified (one minute in this case).
Typically you'd check the return value, so you can tell the difference between the handle being signaled and the wait timing out, but in your case you immediately call po.CancellationToken.ThrowIfCancellationRequested();, so it seems reasonable to me to just ignore the return value from the Wait() method and let the next program statement take care of actually interrupting the method.
What is the use of CancellationToken's IsCancellationRequested property? Consider below code
static void Main(string[] args)
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Console.WriteLine("Press Enter to Start.\nAgain Press enter to finish.");
Console.ReadLine();
Task t = new Task(() =>
{
int i = 0;
while (true)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("Task Cancel requested");
break;
}
Console.WriteLine(i++);
}
}, token);
t.Start();
// wait for input before exiting
Console.ReadLine();
tokenSource.Cancel();
if(t.Status==TaskStatus.Canceled)
Console.WriteLine("Task was cancelled");
else
Console.WriteLine("Task completed");
}
I find that on rare occasions code inside if block doesn't run. If so what is the use of polling to see if cancellation is requested?
The problem with your code is that you don't wait for the Task to finish. So, what can happen is this:
You call Cancel().
You check Status, which returns Running.
Confusingly, you write “Task completed” when the Task is still running.
Main() completes, application exits.
(At this point, IsCancellationRequested would be checked from the background thread. But that never happens, since the application already exited.)
To fix that, add t.Wait() after you call Cancel().
But that still won't fix your program completely. You need to tell the Task that it was canceled. And you do that by throwing OperationCanceledException that contains the CancellationToken (the usual way to do that is to call ThrowIfCancellationRequested()).
One issue with that is that Wait()ing on a Task that was canceled will throw an exception, so you will have to catch that.
I tried to run a simple example on the cancellation of a task like the one below
CancellationTokenSource tokenSource2 = new CancellationTokenSource();
CancellationToken token2 = tokenSource2.Token;
Task task2 = new Task(() =>
{
for (int i = 0; i < int.MaxValue; i++)
{
token2.ThrowIfCancellationRequested();
Thread.Sleep(100);
Console.WriteLine("Task 2 - Int value {0}", i);
}
}, token2);
task2.Start();
Console.WriteLine("Press any key to cancel the task");
Console.ReadLine();
tokenSource2.Cancel();
Console.WriteLine("Task 2 cancelled? {0}", task2.IsCanceled);
I expected that Console.WriteLine("Task 2 cancelled? {0}", task2.IsCanceled); would print **"Task 2 cancelled? True"**, but it printed "False".
Do you know what happened? Is that the expected behaviour? Thanks.
EDIT: to ensure that the task hasn't completed before the cancellation request is called. I added the Console.ReadLine().
First, maybe you misunderstand what IsCanceled means? It doesn't mean “this Task is pending cancellation, so it should complete shortly”, it means “this Task has been canceled, it is now complete”.
If you didn't misunderstand that, think about what exactly the sequence of events is. What happens is this:
ThrowIfCancellationRequested() is called, but the token wasn't canceled yet, so it doesn't throw.
Thread.Sleep() is called, so the thread running the Task sleeps.
Cancel() is called.
IsCanceled is checked. The code in the Task didn't have a chance to realize that the token was canceled, so it's still running, so IsCanceled returns false.
ThrowIfCancellationRequested() is called again, this time it throws, which actually cancels the Task.
This is why ISCanceled is returning false to you. If you want it to return true, you could add something like Thread.Sleep(150) before checking IsCanceled, or, even better, actually wait for the Task to complete.
The Task ended before you call for cancellation, take a look at the following below this may help to shed some more light on how to resolve your issue
By reading from here http://social.msdn.microsoft.com/Forums/da-DK/parallelextensions/thread/9f88132a-f8bd-4885-ab63-645d7b6c2127 it seems that the token is used to cancel the task BEFORE the task is "really" started, but after it has been queued.
It's more a way to cancel a task that's scheduled to occur, but not started yet. Once the task is running, the only way to cancel it is cooperatively via your own checking within the method. Without this, you'd have to always start the task, then check it internally, which would add a lot of extra, unnecessary overhead
You can even read it from Cancellation token in Task constructor: why?
This is not an answer but too long to write in a comment
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = cancellationTokenSource.Token;
Task task = new Task(() =>
{
for (int i = 0; i < int.MaxValue; i++)
{
cancellationToken.ThrowIfCancellationRequested();
Console.WriteLine("Task 2 - Int value {0}", i);
}
}, cancellationToken);
task.Start();
cancellationTokenSource.Cancel();
try
{
task.Wait();
}
catch (AggregateException ae)
{
if(ae.InnerExceptions.Single() is TaskCanceledException)
Console.WriteLine("Caught TaskCanceledException");
else
Console.WriteLine("Did not catch canceled");
}
Console.WriteLine("Task 2 cancelled? {0}", task.IsCanceled);
The code above prints what is expected, and I never got a print of 'Task 2 - Int value {0}' so I don't think it finished before canceling.
Note that canceling the task throws an exception in my sample
There are some patterns for handling exceptions using tasks that can be good to read about.