CancellationToken Cancel not breaking out of BlockingCollection - c#

I have a cancellation token like so
static CancellationTokenSource TokenSource= new CancellationTokenSource();
I have a blocking collection like so
BlockingCollection<object> items= new BlockingCollection<object>();
var item = items.Take(TokenSource.Token);
if(TokenSource.CancelPending)
return;
When I call
TokenSource.Cancel();
The Take does not continue like it should. If I use the TryTake with a poll the Token shows it is being set as Canceled.

That's working as expected. If the operation is canceled, items.Take will throw OperationCanceledException. This code illustrates it:
static void DoIt()
{
BlockingCollection<int> items = new BlockingCollection<int>();
CancellationTokenSource src = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem((s) =>
{
Console.WriteLine("Thread started. Waiting for item or cancel.");
try
{
var x = items.Take(src.Token);
Console.WriteLine("Take operation successful.");
}
catch (OperationCanceledException)
{
Console.WriteLine("Take operation was canceled. IsCancellationRequested={0}", src.IsCancellationRequested);
}
});
Console.WriteLine("Press ENTER to cancel wait.");
Console.ReadLine();
src.Cancel(false);
Console.WriteLine("Cancel sent. Press Enter when done.");
Console.ReadLine();
}

Related

Cancel Token from infinite Parallel.Foreach loop from an event

I have write some code, where i am using Parallel.Foreach for few items to work parallel with infinite loop i.e working fine after every 60 sec.
But here my message can be change by the user at any time and i need to re-process with new message.
For this, i need to cancel the infinite Parallel.Foreach loop to reprocess the updated message.
when i am trying to reprocess the main method its working fine for new message, but its running twice because the previous scheduled tasks is not canceled. I am assuming i need to cancel process from Parrallel.Foreach loop and re-run again for updated message with new schedule.
So can anyone help me to cancel the queued task that is already scheduled for next 60 second.
static void Main(string[] args)
{
List<RealTimeMessage> messages = GetRealTimeMessage();
Parallel.ForEach(messages, (message) =>
{
processMessage(message);
});
Console.ReadLine();
}
private static async void processMessage(RealTimeMessage message)
{
try
{
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(60));
await Task.Run(() => ProceesRequest(message));
}
}
catch (Exception)
{
Console.WriteLine("Critical error");
}
}
private static List<RealTimeMessage> GetRealTimeMessage()
{
List<RealTimeMessage> realTimeMessages = new List<RealTimeMessage>();
realTimeMessages.Add(new RealTimeMessage { MessageText = "Message 4", IntervalTime = "1", MessageType = "AIDX", TimeOfDay = "" });
realTimeMessages.Add(new RealTimeMessage { MessageText = "Message 5", IntervalTime = "2", MessageType = "AMSX", TimeOfDay = "" });
return realTimeMessages;
}
private static void ProceesRequest(RealTimeMessage message)
{
// do domething
}
This is a misuse of Parallel.ForEach, use Task.WhenAll instead
Don't start a Task in ProcessMessage (this could be intentional, however it looks like a mistake).
Use a CancellationToken to cancel a task
Don't use async void unless it's for an event
Use standard casing for method names
Don't use while(true) use while (!token.IsCancellationRequested)
When all things are considered, it would look something like this
static async Task Main(string[] args)
{
var ts = new CancellationTokenSource();
var messages = GetRealTimeMessage();
var tasks = messages.Select(x => ProcessMessage(x, ts.Token));
Console.WriteLine("Press any key to cancel tasks")
Console.ReadKey();
ts.Cancel();
await Task.WhenAll(tasks);
Console.WriteLine("All finished");
Console.ReadKey();
}
private static async Task ProcessMessage( RealTimeMessage message, CancellationToken token )
{
try
{
while (!token.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromSeconds(60), token);
ProcessRequest(message);
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation Cancelled");
}
catch (Exception ex)
{
Console.WriteLine("Critical error: " + ex.Message);
}
}
To cancel your tasks, just call ts.Cancel().

how to cancel Task from another Task?

try{
var cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
Task.Run(()=>
{
//DoSomething(); excute long time
}, ct);
Task.Run(()=>
{
Thread.Sleep(1000);
cts.Cancel();
}, ct).Wait();
}
catch (OperationCanceledException ex)
{
Console.WriteLine("exception" + ex.Message);
}
finally
{
Console.WriteLine("finally");
}
When I call cts.Cancel()
DoSomething still Work.....................
how can i stop first Task?
if DoSomething Has a loop
I can add ct.ThrowIfCancellationRequested() , it's working
but DoSomething not a loop , what can i do ?
Whether or not DoSomething() is a loop, it must explicitly check and respond to the IsCancellationRequested property on the cancellation Token. If this property is true, the function must return as soon as practical, even if it means not completing the full execution. Note that DoSomething() is not a loop.
void DoSomething(System.Threading.CancellationToken tok)
{
Thread.Sleep(900);
if (tok.IsCancellationRequested)
return;
Console.WriteLine("after 1");
Thread.Sleep(1800);
if (tok.IsCancellationRequested)
return;
Console.WriteLine("after 2");
}
void Main()
{
try
{
var cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
System.Threading.Tasks.Task.Run(() =>
{
DoSomething(ct);
//DoSomething(); excute long time
});
System.Threading.Tasks.Task.Run(() =>
{
Thread.Sleep(1000);
cts.Cancel();
}).Wait();
}
catch (OperationCanceledException ex)
{
Console.WriteLine("exception" + ex.Message);
}
finally
{
Console.WriteLine("finally");
}
}
Note: DoSomething() must reference the cancellation token and explicitly check the IsCancellationRequested property. The role of the cancellation token in the Task.Run() is explained in this answer: https://stackoverflow.com/a/3713113/41410, but it doesn't play a role is cancelling the flow of DoSomething()

when Task.IsCancelled is set as true?

when will come Task.IsCanceled = true;
Code:
var cts = new CancellationTokenSource();
string result = "";
cts.CancelAfter(10000);
try
{
Task t = Task.Run(() =>
{
using (var stream = new WebClient().OpenRead("http://www.rediffmail.com"))
{
result = "success!";
}
cts.Token.ThrowIfCancellationRequested();
}, cts.Token);
Stopwatch timer = new Stopwatch();
timer.Start();
while (timer.IsRunning)
{
if (timer.ElapsedMilliseconds <= 10000)
{
if (result != ""){
timer.Stop();
Console.WriteLine(result);
}
}
else
{
timer.Stop();
//cts.Cancel();
//cts.Token.ThrowIfCancellationRequested();
}
}
}
catch (OperationCanceledException)
{
Console.WriteLine(t.IsCanceled); // still its appear in false.
}
My requirement is - Task is not completed upto 10seconds, Need to cancel the task.
So I am setting timer and watch upto the given seconds. its not completed mean cancel the task and showing error message.
You have to pass the token to your method. It should inspect the token and respect the call to Cancel() of the CancellationTokenSource.
Or you do it yourself:
Task t = Task.Factory.StartNew(() =>
{
myResult = method(); // Request processing in parallel
cts.Token.ThrowIfCancellationRequested(); // React on cancellation
}, cts.Token);
A complete example is this:
async Task Main()
{
var cts = new CancellationTokenSource();
var ct = cts.Token;
cts.CancelAfter(500);
Task t = null;
try
{
t = Task.Run(() => { Thread.Sleep(1000); ct.ThrowIfCancellationRequested(); }, ct);
await t;
Console.WriteLine(t.IsCanceled);
}
catch (OperationCanceledException)
{
Console.WriteLine(t.IsCanceled);
}
}
The output is that an OperationCanceledException is thrown and the result is
True
if you remove the ct.ThrowIfCancellationRequested(); part it will show
False
Edit:
Now, you have updated the question, some comments on that. First, you won't need the timer anymore since you are using the CancelAfter method. Second, you need to await your task. So that makes something like this:
string result = "";
cts.CancelAfter(10000);
Task t = null;
try
{
t = Task.Run(() =>
{
using (var stream = new WebClient().OpenRead("http://www.rediffmail.com"))
{
cts.Token.ThrowIfCancellationRequested();
result = "success!";
}
}, cts.Token);
await t;
}
catch (OperationCanceledException)
{
Console.WriteLine(t.IsCanceled);
}
This should show that t.IsCanceled is true but of course only when the call of the WebClient takes longer that 10 seconds.
From documentation:
A Task will complete in the TaskStatus.Canceled state under any of the following conditions:
Its CancellationToken was marked for cancellation before the task started executing.
The task acknowledged the cancellation request on its already signaled CancellationToken by throwing an OperationCanceledException that bears the same CancellationToken.
The task acknowledged the cancellation request on its already signaled CancellationToken by calling the ThrowIfCancellationRequested method on the CancellationToken.
So basically you would need to throw an OperationCanceledException within your task to force the state for instance by executing cts.Token.ThrowIfCancellationRequested() just after you cancel it.
But the intention of this mechanism is a bit the other way around. You cancel source say while user presses cancel button on your form (from outside of your task) an task just verifies if cancellation was requested in some safe to cancel points of its code.

Continuation for Cancelled Task

I am trying to set the continuation task after it got cancelled. But I am not able to handle the exception internally.
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;
Task task = Task.Run(() =>
{
while (!token.IsCancellationRequested)
{
Console.Write("*");
Thread.Sleep(1000);
}
throw new OperationCanceledException();
}, token).ContinueWith((t) =>
{
t.Exception.Handle((e) => true);
Console.WriteLine("You have cancelled the task");
}, TaskContinuationOptions.OnlyOnCanceled);
Console.WriteLine("Press enter to stop the task");
Console.ReadLine();
cancellationTokenSource.Cancel();
task.Wait();
Console.WriteLine("Press enter to end the application");
Console.ReadLine();
The exception is not handled by the code. If i wrap the code with a try block I get an error saying "Object instance not set to the reference of an object".
Any idea?

Correct way to delay the start of a Task

I want to schedule a task to start in x ms and be able to cancel it before it starts (or just at the beginning of the task).
The first attempt would be something like
var _cancelationTokenSource = new CancellationTokenSource();
var token = _cancelationTokenSource.Token;
Task.Factory.StartNew(() =>
{
token.ThrowIfCancellationRequested();
Thread.Sleep(100);
token.ThrowIfCancellationRequested();
}).ContinueWith(t =>
{
token.ThrowIfCancellationRequested();
DoWork();
token.ThrowIfCancellationRequested();
}, token);
But I feel like there should be a better way, as this would use up a thread while in the sleep, during which it could be canceled.
What are my other options?
Like Damien_The_Unbeliever mentioned, the Async CTP includes Task.Delay. Fortunately, we have Reflector:
public static class TaskEx
{
static readonly Task _sPreCompletedTask = GetCompletedTask();
static readonly Task _sPreCanceledTask = GetPreCanceledTask();
public static Task Delay(int dueTimeMs, CancellationToken cancellationToken)
{
if (dueTimeMs < -1)
throw new ArgumentOutOfRangeException("dueTimeMs", "Invalid due time");
if (cancellationToken.IsCancellationRequested)
return _sPreCanceledTask;
if (dueTimeMs == 0)
return _sPreCompletedTask;
var tcs = new TaskCompletionSource<object>();
var ctr = new CancellationTokenRegistration();
var timer = new Timer(delegate(object self)
{
ctr.Dispose();
((Timer)self).Dispose();
tcs.TrySetResult(null);
});
if (cancellationToken.CanBeCanceled)
ctr = cancellationToken.Register(delegate
{
timer.Dispose();
tcs.TrySetCanceled();
});
timer.Change(dueTimeMs, -1);
return tcs.Task;
}
private static Task GetPreCanceledTask()
{
var source = new TaskCompletionSource<object>();
source.TrySetCanceled();
return source.Task;
}
private static Task GetCompletedTask()
{
var source = new TaskCompletionSource<object>();
source.TrySetResult(null);
return source.Task;
}
}
Since .NET 4.5 has now been released, there's a very simple built-in way to delay a task: just use Task.Delay(). behind the scenes, it uses the implementation that ohadsc decompiled.
The correct answer in the future will probably be Task.Delay. However, that's currently only available through the Async CTP (and in the CTP, it's on TaskEx rather than Task).
Unfortunately, because it's only in CTP, there aren't many good links to documentation for it either.
Look at the TaskFactoryExtensions_Delayed in "Parallel Programming with .NET 4 Samples".
I haven't tested this, but here is a first-pass at wrapper methods to create an initial 'Delay' Task or to continue after a Delay. If you find issues, feel free to correct.
public static Task StartDelayTask(int delay, CancellationToken token)
{
var source = new TaskCompletionSource<Object>();
Timer timer = null;
timer = new Timer(s =>
{
source.TrySetResult(null);
timer.Dispose();
}, null, delay, -1);
token.Register(() => source.TrySetCanceled());
return source.Task;
}
public static Task ContinueAfterDelay
(this Task task,
int delay, Action<Task> continuation,
CancellationToken token)
{
var source = new TaskCompletionSource<Object>();
Timer timer = null;
var startTimer = new Action<Task>(t =>
{
timer = new Timer(s =>
{
source.TrySetResult(null);
timer.Dispose();
},null,delay,-1);
});
task.ContinueWith
(startTimer,
token,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.Current);
token.Register(() => source.TrySetCanceled());
return source.Task.ContinueWith(continuation, token);
}
You can use Token.WaitHandle.WaitOne(int32 milliseconds) overload method to specify number of milliseconds to wait for your task. But key difference between Thread.Sleep(xxx) and Token.WaitHandle.WaitOne(xxx) that later blocks thread until the time specified elapsed or the token has been canceled.
Here is an example
void Main()
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var task = Task.Factory.StartNew(() =>
{
// wait for 5 seconds or user hit Enter key cancel the task
token.WaitHandle.WaitOne(5000);
token.ThrowIfCancellationRequested();
Console.WriteLine("Task started its work");
});
Console.WriteLine("Press 'Enter' key to cancel your task");
Console.Read();
tokenSource.Cancel();
}

Categories