I developed a C# based Windows Service which runs all of its logic in several different tasks.
To allow the service to shutdown gracefully when it is being stopped, I am using a CancellationToken which is passed to any function that accepts one (mostly from 3rd party libraries which I am using) in order to abort processing before completion.
I noticed that none of those functions throw an OperationCanceledException when the cancellation is requested while the function is being called, so my application simply continues executing until I call ThrowIfCancellationRequested() somewhere else later in my code. Am I supposed to manually call ThrowIfCancellationRequested() after calling every single of those functions to make sure that the tasks stop as soon as possible, or when exactly am I supposed to call ThrowIfCancellationRequested() in my own code?
Yes, you are supposed to call ThrowIfCancellationRequested() manually, in the appropriate places in your code (where appropriate is determined by you as a programmer).
Consider the following example of a simple job processing function that reads jobs from a queue and does stuff with them. The comments illustrate the sort of thinking the developer might go through when deciding whether to check for cancellation.
Note also that you are right - the standard framework functions that accept a token will not throw a cancellation exception - they will simply return early, so you have to check for cancellation yourself.
public async Task DoWork(CancellationToken token)
{
while(true)
{
// It is safe to check the token here, as we have not started any work
token.ThrowIfCancellationRequested();
var nextJob = GetNextJob();
// We can check the token here, because we have not
// made any changes to the system.
token.ThrowIfCancellationRequested();
var jobInfo = httpClient.Get($"job/info/{nextJob.Id}", token);
// We can check the token here, because we have not
// made any changes to the system.
// Note that HttpClient won't throw an exception
// if the token is cancelled - it will just return early,
// so we must check for cancellation ourselves.
token.ThrowIfCancellationRequested();
// The following code is a critical section - we are going to start
// modifying various databases and things, so don't check for
// cancellation until we have done it all.
ModifySystem1(nextJob);
ModifySystem2(nextJob);
ModifySystem3(nextJob);
// We *could* check for cancellation here as it is safe, but since
// we have already done all the required work *and* marking a job
// as complete is very fast, there is not a lot of point.
MarkJobAsCompleted(nextJob);
}
}
Finally, you might not want to leak cancellation exceptions from your code, because they aren't "real" exceptions - they are expected to occur whenever someone stops your service.
You can catch the exception with an exception filter like so:
public async Task DoWork(CancellationToken token)
{
try
{
while(true)
{
// Do job processing
}
}
catch (OperationCanceledException e) when (e.CancellationToken == token)
{
Log.Info("Operation cancelled because service is shutting down.");
}
catch (Exception e)
{
Log.Error(e, "Ok - this is actually a real exception. Oh dear.");
}
}
Related
I'm really new to threading multitasking/multithreading, but I'm working on a project where I think I need it. The user will be editing a fairly complex diagram, and I want the program to check for validity of the diagram. The validity check is non-trivial (polynomial time, though, not NP - seconds, not minutes or years, but I don't want to hold the user up for a few seconds after every change) so I would like the program to check for validity in the background and highlight inconsistencies when it finds them. When the user makes certain changes to the diagram (changes the structure, not just the labels on elements), the validation will have to throw away what it was doing and start again. I'm assuming the user will eventually take a break to think/go for a pee/go for a coffee/chat to that rather cute person two cubicles along, but in case they don't, I have to let the validation run to completion in some circumstances (before a save or a printout, for example). Broad-brush, what are the features of C# I'll need to learn, and how do I structure that?
Broad Brush. Here we go.
Q: "What are the features of C# I'll need to learn?"
A: You can get by nicely with a basic toolkit consisting (roughly speaking) of:
System.Threading.Tasks.Task
System.Threading.CancellationTokenSource
System.Threading.SemaphoreSlim
Q: "I don't want to hold the user up for a few seconds after every change"
A: OK, so we will never-ever block the UI thread. Fire off a Task to run a background validation routine that checks every now and then to see if it's been cancelled.
CancellationTokenSource _cts = null;
SemaphoreSlim ssBusy = new SemaphoreSlim(2);
private void ExecValidityCheck()
{
ssBusy.Wait();
Task.Run(() =>
{
try
{
_cts = new CancellationTokenSource();
LongRunningValidation(_cts.Token);
}
finally
{
ssBusy.Release();
}
})
.GetAwaiter()
.OnCompleted(CheckForRestart);
}
We'll call CheckForRestart using GetAwaiter().OnCompleted(). This just means that without blocking we'll be notified as a callback when the thread finishes for one of three reasons:
Cancelled
Cancelled, but with an intent to start the validation over from the beginning.
Ran validation to completion
By calling CheckForRestart we determine whether to start it over again or not.
void CheckForRestart()
{
BeginInvoke((MethodInvoker)delegate
{
if (_restart)
{
_restart = false;
ExecValidityCheck();
}
else
{
buttonCancel.Enabled = false;
}
});
}
Rather that post the complete code here, I pushed a simple working example to our GitHub. You can browse it there or clone and run it. 20-second screen capture. When the RESTART button is clicked in the video, it's checking the CurrentCount property of the Semaphore. In a threadsafe way it determines whether the validation routine is already running or not.
I hope I've managed to give you a few ideas about where to start. Sure, the explanation I've given here has a few holes but feel free to address your critical concerns in the comments and I'll try to respond.
You probably need to learn about asynchronous programming with async/await, and about cooperative cancellation. The standard practice for communicating cancellation is by throwing an OperationCanceledException. Methods that are intended to be cancelable accept a CancellationToken as argument, and observe frequently the IsCancellationRequested method of the token. So here is the basic structure of a cancelable Validate method with a boolean result:
bool Validate(CancellationToken token)
{
for (int i = 0; i < 50; i++)
{
// Throw an OperationCanceledException if cancellation is requested
token.ThrowIfCancellationRequested();
Thread.Sleep(100); // Simulate some CPU-bound work
}
return true;
}
The "driver" of the CancellationToken is a class named CancellationTokenSource. In your case you'll have to create multiple instances of this class, one for every time that the diagram is changed. You must store them somewhere so that you can call later their Cancel method, so lets make two private fields inside the Form, one for the most recent CancellationTokenSource, and one for the most recent validation Task:
private Task<bool> _validateTask;
private CancellationTokenSource _validateCTS;
Finally you'll have to write the logic for the event handler of the Diagram_Changed event. It is probably not desirable to have multiple validation tasks running side by side, so it's a good idea to await for the completion of the previous task before launching a new one. It is important that awaiting a task doesn't block the UI. This introduces the complexity that multiple Diagram_Changed events, along with other unrelated events, can occur before the completion of the code inside the handler. Fortunately you can count on the single-threaded nature of the UI, and not have to worry about the thread-safety of accessing the _validateTask and _validateCTS fields by multiple asynchronous workflows. You do need to be aware though that after every await these fields may hold different values than before the await.
private async void Diagram_Changed(object sender, EventArgs e)
{
bool validationResult;
using (var cts = new CancellationTokenSource())
{
_validateCTS?.Cancel(); // Cancel the existing CancellationTokenSource
_validateCTS = cts; // Publish the new CancellationTokenSource
if (_validateTask != null)
{
// Await the completion of the previous task before spawning a new one
try { await _validateTask; }
catch { } // Ignore any exception
}
if (cts != _validateCTS) return; // Preempted (the event was fired again)
// Run the Validate method in a background thread
var task = Task.Run(() => Validate(cts.Token), cts.Token);
_validateTask = task; // Publish the new task
try
{
validationResult = await task; // Await the completion of the task
}
catch (OperationCanceledException)
{
return; // Preempted (the validation was canceled)
}
finally
{
// Cleanup before disposing the CancellationTokenSource
if (_validateTask == task) _validateTask = null;
if (_validateCTS == cts) _validateCTS = null;
}
}
// Do something here with the result of the validation
}
The Validate method should not include any UI manipulation code, because it will be running in a background thread. Any effects to the UI should occur after the completion of the method, through the returned result of the validation task.
Background:
I have a web application which kicks off long running (and stateless) tasks:
var task = Task.Run(() => await DoWork(foo))
task.Wait();
Because they are long running, I need to be able to cancel them from a separate web request.
For this, I would like to use a CancellationToken and just throw an exception as soon as the token is canceled. However, from what I've read, Task Cancellation is cooperative, meaning the code the task is running must explicitly check the token to see if a cancellation request has been made (for example CancellationToken.ThrowIfCancellation())
I would like to avoid checking CancellationToken.ThrowIfCancellation() all over the place, since the task is quite long and goes through many functions. I think I can accomplish what I want creating an explicit Thread, but I would really like to avoid manual thread management. That said...
Question:
Is it possible to automatically throw an exception in the task when it has been canceled, and if not, are there any good alternatives (patterns, etc.) to reduce polluting the code with CancellationToken.ThrowIfCancellation()?
I'd like to avoid something like this:
async Task<Bar> DoWork(Foo foo)
{
CancellationToken.ThrowIfCancellation()
await DoStuff1();
CancellationToken.ThrowIfCancellation()
await DoStuff2();
CancellationToken.ThrowIfCancellation()
await DoStuff3();
...
}
I feel that this question is sufficiently different from this one because I'm explicitly asking for a way to minimize calls to check the cancellation token, to which the accepted answer responds "Every now and then, inside the functions, call token.ThrowIfCancellationRequested()"
Is it possible to automatically throw an exception in the task when it has been canceled, and if not, are there any good alternatives (patterns, etc.) to reduce polluting the code with CancellationToken.ThrowIfCancellation()?
No, and no. All cancellation is cooperative. The best way to cancel code is to have the code respond to a cancellation request. This is the only good pattern.
I think I can accomplish what I want creating an explicit Thread
Not really.
At this point, the question is "how do I cancel uncancelable code?" And the answer to that depends on how stable you want your system to be:
Run the code in a separate Thread and Abort the thread when it is no longer necessary. This is the easiest to implement but the most dangerous in terms of application instability. To put it bluntly, if you ever call Abort anywhere in your app, you should regularly restart that app, in addition to standard practices like heartbeat/smoketest checks.
Run the code in a separate AppDomain and Unload that AppDomain when it is no longer necessary. This is harder to implement (you have to use remoting), and isn't an option in the Core world. And it turns out that AppDomains don't even protect the containing application like they were supposed to, so any apps using this technique also need to be regularly restarted.
Run the code in a separate Process and Kill that process when it is no longer necessary. This is the most complex to implement, since you'll also need to implement some form of inter-process communication. But it is the only reliable solution to cancel uncancelable code.
If you discard the unstable solutions (1) and (2), then the only remaining solution (3) is a ton of work - way, way more than making the code cancelable.
TL;DR: Just use the cancellation APIs the way they were designed to be used. That is the simplest and most effective solution.
If you actually just have a bunch of method calls you are calling one after the other, you can implement a method runner that runs them in sequence and checks in between for the cancellation.
Something like this:
public static void WorkUntilFinishedOrCancelled(CancellationToken token, params Action[] work)
{
foreach (var workItem in work)
{
token.ThrowIfCancellationRequested();
workItem();
}
}
You could use it like this:
async Task<Bar> DoWork(Foo foo)
{
WorkUntilFinishedOrCancelled([YourCancellationToken], DoStuff1, DoStuff2, DoStuff3, ...);
}
This would essentially do what you want.
If you are OK with the implications of Thread.Abort (disposables not disposed, locks not released, application state corrupted), then here is how you could implement non-cooperative cancellation by aborting the task's dedicated thread.
private static Task<TResult> RunAbortable<TResult>(Func<TResult> function,
CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<TResult>();
var thread = new Thread(() =>
{
try
{
TResult result;
using (cancellationToken.Register(Thread.CurrentThread.Abort))
{
result = function();
}
tcs.SetResult(result);
}
catch (ThreadAbortException)
{
tcs.TrySetCanceled();
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
});
thread.IsBackground = true;
thread.Start();
return tcs.Task;
}
Usage example:
var cts = new CancellationTokenSource();
var task = RunAbortable(() => DoWork(foo), cts.Token);
task.Wait();
So, according to an answer to this post :
2) If the body of the task is also monitoring the cancellation token
and throws an OperationCanceledException containing that token (which
is what ThrowIfCancellationRequested does), then when the task sees
that OCE, it checks whether the OCE's token matches the Task's token.
If it does, that exception is viewed as an acknowledgement of
cooperative cancellation and the Task transitions to the Canceled
state (rather than the Faulted state).
From this I understood that by passing a token to the constructor of the task and then calling that same token's ThrowIfCancellationRequested() method, the task would in fact terminate peacefully, without me having to catch the OperationCanceledException explicitly.
However as it turns out, an exception is thrown, so I believe I may have misunderstood the mechanic.
My code:
public void AI()
{
IsBusy = true;
var token = stopGameCancellationTokenSource.Token;
var workTask = new Task(() => aiWork(token), token);
workTask.Start();
workTask.ContinueWith(task => { IsBusy = false; });
}
private void aiWork(CancellationToken token)
{
while ( true)
{
//Some computation being done here
token.ThrowIfCancellationRequested(); //Exception is thrown here, I thought it wouldn't
//More computation here, which I don't want to happen if cancellation has benn requested
}
}
This line
token.ThrowIfCancellationRequested();
explicitly throws an exception. What the link was telling you is that if the token of the task matches the token in the OperationCanceledException that has just been thrown, "the Task transitions to the Canceled state (rather than the Faulted state)."
So the bottom line is if you don't want an exception to be thrown when the task is canceled, simply omit that line!
In addition to the explanation in #JurgenCamilleri answer of why you are getting the error, what you likely intended to do was loop until cancellation was requested. You can do that by changing your code to something like this:
private void aiWork(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
//Some computation being done here
if (token.IsCancellationRequested)
break; // need to cancel
//More computation here, which I don't want to happen if cancellation has been requested
}
}
As the name of the method suggests, ThrowIfCancellationRequested will throw an exception (OperationCanceledException) if a cancelletion was requested.
If you really don't want an exception to be thrown, you can check if token.IsCancellationRequested is true and, in this case, exit your function.
However, I'd recommand sticking with token.ThrowIfCancellationRequested() unless you got good reasons not to.
Using .NET TPL DataFlow blocks.
Is there any way to timeout the processing of a message?
e.g.
lets say I have a BufferBlock<T>, is it possible to link that to another block that processes one message at a time (MaxDegreeOfParallellism 1) and force a timeout if the processing runs for too long?
Or is it even possible to do using the BufferBlock only?
I suspect I can use a cancellation token somehow and a delay, but not sure how this would be done.
Also, how expensive would such timeout be? would it add much overhead to the message processing time?
Many methods of BufferBlock<T> do accept CancellationToken, and I believe that would be the proper way of timing out an operation. E.g.:
var cts = new CancellationTokenSource(5000); // cancel in 5s
// Alternatively: cts.CancelAfter(5000);
try
{
var output = await bufferBlock.ReceiveAsync(cts.Token);
}
catch (Exception ex)
{
// check if ex is OperationCanceledException,
// which could be wrapped with AggregateException
}
IMO, the only way of evaluating its efficiency would be to run some profiling tests.
[UPDATE] Based upon the comments, if you're looking to time-out the pipeline processing, you can probably do that when you construct your ActionBlock object and provide it with an instance of ExecutionDataflowBlockOptions. At that point, you can supply DataflowBlockOptions.CancellationToken and use it in the same way as described above. Also, you could pass a CancellationToken into LinkTo as a part of DataflowLinkOptions.
Once you've provided the pipeline with a CancellationToken, you can track the status of ActionBlock.Completion/TransformBlock.Completion, which is a Task, so you can await it and catch a cancellation exception, or use ContinueWith with it (if that's what you mean under some way to tell if the "processing" of the message times out).
Disclaimer: I haven't tried this myself and would be interested to know whether it works as expected.
I have this code that start a Task thread that calls a method to read data in a StreamSocket whenever it becomes available. Iy also takes a a cancellation token.
await Task.Factory.StartNew(ProcessMessage, CancelToken);
But after i call this method.
CancelToken.Cancel();
My app stills run the ProcessMessage method when i get new data in the StreamSocket. Which shouldn't be happening. It think its because i use await on it.
How can I get this task to stop when the Token is cancelled?
When you issue CancellationToken.Cancel(), the IsCancellationRequested property on every copy of the cancellation token is set to true. The objects that receive the notification can respond in whatever manner is appropriate. The typical pattern is to call token.ThrowIfCancellationRequested() within your loop somewhere.
So in your ProcessMessage routine, you need something like
while (processing)
{
// carry on your processing...
// Poll on this property if you have to do other cleanup before throwing.
if (token.IsCancellationRequested)
{
// Clean up here, then...
token.ThrowIfCancellationRequested();
}
}
You can find guidelines on implementing cancellation with await in the MSDN documentation.