I am writing a windows service and found an example which suggests writing a polling windows services as follows:
private void Poll()
{
CancellationToken cancellationPoll = ctsPoll.Token;
while (!cancellationPoll.WaitHandle.WaitOne(tsInterval))
{
PollDatabase();
// Occasionally check the cancellation state.
if (cancellationPoll.IsCancellationRequested)
{
break;
}
}
}
I am a little confused when it comes to the cancellation and if i need both the cancellationPoll.WaitHandle.WaitOne() and cancellationPoll.IsCancellationRequested or are they doing the same thing and only one is required?
The !cancellationPoll.WaitHandle.WaitOne(tsInterval) is there to ensure the polling interval so you will have at least tsIntetval between polling(+ the operation duration):
--tsInterval--|--operation--|--tsInterval--|...
If you look at the documentation for CancellationToken.WaitHandle it says the following:
A WaitHandle that is signaled when the token is canceled.
So in your case the operation cancellationPoll.IsCancellationRequested is sufficient because you don't have anything after it. But imagine the situation like this:
while (!cancellationPoll.WaitHandle.WaitOne(tsInterval))
{
//long operation A
if (cancellationPoll.IsCancellationRequested)
{
break;
}
//long operation B
if (cancellationPoll.IsCancellationRequested)
{
break;
}
//long operation C
}
In this case it makes sense to occasionally check the cancellation state to avoid running long operation...
The waiting of WaitHanlde is redundant here as from the result standpoint it does the same as IsCancellationRequested - indicates that cancellation is requested (but does it in slightly different way). So for your case you can choose single method: either WaitHandle or IsCancellationRequested. But please keep in mind that WaitHandle is IDisposable and requires disposing the associated CancellationTokenSource. If you choose to use IsCancellationRequested don't forget to add a call which is supposed to reschedule thread such as Thread.Sleep in order not to over-utilize the CPU resources.
One of scenario when WaitHanlde can be applied is when you need to wait for a handle and would like to introdece cancellation semantic to this wait:
WaitHandle.WaitAny(new [] { handleToWait, cancellationHandle });
The !cancellationPoll.WaitHandle.WaitOne(tsInterval) is needed so that you do not wait the whole time. WaitOne(tsInterval) will either return because the token recevied a singnal to cancel or because the time is run out. If the token recieved a signal to cancel WaitOne(tsInterval) will return true and so end the loop.
For example, if you would do something like:
while(true)
{
// long operation
if (cancellationPoll.IsCancellationRequested)
{
break;
}
Thread.Sleep(tsInterval);
}
if then the cancellation is reqested while the thread is blocked by Thread.Sleep() the whole operation would not know that an cancellation is reqested not until Thread.Sleep() is finished and the next loop run has come to the if statement.
Related
I have a couple of hundred devices and I need to check their status every 5 seconds.
The API I'm using contains a blocking function that calls a dll and returns a status of a single device
string status = ReadStatus(int deviceID); // waits here until the status is returned
The above function usually returns the status in a couple of ms, but there will be situations where I might not get the status back for a second or more! Or even worse, one device might not respond at all.
I therefore need to introduce a form of asynchronicity to make sure that one device that doesn't respond doesn't impend all the others being monitored.
My current approach is as following
// triggers every 5 sec
public MonitorDevices_ElapsedInterval(object sender, ElapsedEventArgs elapsedEventArgs)
{
foreach (var device in lstDevices) // several hundred devices in the list
{
var task = device.ReadStatusAsync(device.ID, cts.Token);
tasks.Add(task);
}
// await all tasks finished, or timeout after 4900ms
await Task.WhenAny(Task.WhenAll(tasks), Task.Delay(4900, cts.Token));
cts.Cancel();
var devicesThatResponded = tasks.Where(t => t.Status == TaskStatus.RanToCompletion)
.Select(t => t.GetAwaiter().GetResult())
.ToList();
}
And below in the Device class
public async Task ReadStatusAsync(int deviceID, CancellationToken tk)
{
await Task.Delay(50, tk);
// calls the dll to return the status. Blocks until the status is return
Status = ReadStatus(deviceID);
}
I'm having several problems with my code
the foreach loops fires a couple of hundred tasks simultaneously, with the callback from the Task.Delay being served by a thread from the thread pool, each task taking a couple of ms.
I see this as a big potential bottleneck. Are there any better approaches?
This might be similar to what Stephen Cleary commented here, but he didn't provide an alternative What it costs to use Task.Delay()?
In case ReadStatus fails to return, I'm trying to use a cancellation token to cancel the thread that sits there waiting for the response... This doesn't seem to work.
await Task.Delay(50, tk)
Thread.Sleep(100000) // simulate the device not responding
I still have about 20 Worker Threads alive (even though I was expecting cts.Cancel() to kill them.
the foreach loops fires a couple of hundred tasks simultaneously
Since ReadStatus is synchronous (I'm assuming you can't change this), and since each one needs to be independent because they can block the calling thread, then you have to have hundreds of tasks. That's already the most efficient way.
Are there any better approaches?
If each device should be read every 5 seconds, then each device having its own timer would probably be better. After a few cycles, they should "even out".
await Task.Delay(50, tk);
I do not recommend using Task.Delay to "trampoline" non-async code. If you wish to run code on the thread pool, just wrap it in a Task.Run:
foreach (var device in lstDevices) // several hundred devices in the list
{
var task = Task.Run(() => device.ReadStatus(device.ID, cts.Token));
tasks.Add(task);
}
I'm trying to use a cancellation token to cancel the thread that sit there waiting for the response... This doesn't seem to work.
Cancellation tokens do not kill threads. If ReadStatus observes its cancellation token, then it should cancel; if not, then there isn't much you can do about it.
Thread pool threads should not be terminated; this reduces thread churn when the timer next fires.
As you can see in this Microsoft example page of a cancellation token, the doWork method is checking for cancellation on each loop. So, the loop has to start again to cancel out. In your case, when you simulate a long task, it never checks for cancellation at all when it's running.
From How do I cancel non-cancelable async operations?, it's saying at the end : "So, can you cancel non-cancelable operations? No. Can you cancel waits on non-cancelable operations? Sure… just be very careful when you do.". So it answers that we can't cancel it out.
What I would suggest is to use threads with a ThreadPool, you take the starting time of each one and you have an higher priority thread that looks if others bypass their maximum allowed time. If so, Thread.Interrupt().
My question is about task cancellation. I have to poll the token.IsCancellationRequested to detect a cancellation. I call cts.Cancel(); in a WindowsForm Buttonmethod.
Questions:
If I hit the Button is the CancelRequest stored? Or do I have to be lucky, that to same time when I press my Button the code is at the position if (token.IsCancellationRequested)?
Is it possible to cancel my Task with for-loop by event?
Code Example:
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
Task t1 = Task.Factory.StartNew(() =>
{
// Do syncronius work
for(int i=0; i<1000;++i)
{
DoSyncWork(i);
if (token.IsCancellationRequested)
{
Console.WriteLine("Cancelled");
break;
}
Thread.Sleep(1000);
}
});
The cancellation request is a one time thing. Once a token source is canceled it can never be un-canceled so all derived tokens will have IsCancellationRequested always return true.
Yes it is possible, but for a for loop I don't think it is a better way. The way you use a event is you pass the callback to the CancellationToken.Register method and the callback is your event. I leave it to you how you would make a Action delegate cancel the for loop.
A few things with your code that you did not bring up:
You should never call Task.Factory.StartNew without passing in TaskScheduler, if you don't you could cause your code in the StartNew to run on the UI thread when you expect it to be on a background thread. Either use Task.Run or make sure you pass in a scheduler (TaskScheduler.Default is the one Task.Run( uses to always run on the background thread, TaskScheduler.Current is the one that is used when you don't pass anything in and is the one that can cause stuff to run on the UI thread).
If you pass the token in to the factory (or to Task.Run() then use token.ThrowIfCancellationRequested() this will cause the task to enter the Canceled state instead of the Completed state (if you forget to pass it to the factory it will enter the Faulted state), this can be useful for when you need to know when the task finished or not when you are awaiting.
The cancellation is "stored". If you call Cancel() on your CancellationTokenSource instance, the IsCancellationRequested property of the CancellationToken will be true for the rest of its existence.
As I understand it, you want to break your for loop by an event? I don't know how this should look like. The control flow of a for loop is straight forward, no event could break that. But you can use the token in the for loop's header:
for(int i=0; i<1000 && !token.IsCancellationRequested; ++i)
{
...
}
// output log if cancelled
if (token.IsCancellationRequested) Console.WriteLine(...);
if it's that what you want.
The usual implementation for cancelling out with a cancellation token is to throw the OperationCanceledException, by using the tokens .ThrowIfCancellationRequested. This allows you to catch a cancelled operation and get out of operating for however deep you are in the stack and know that it was cancelled.
For your first question, as soon as the token has been cancelled, cancellation will be requested and when you come back around in the loop the if block would be true that you have. Instead of that though I would just use token.ThrowIfCancellationRequested, and catch the specific OperationCanceledException and do any logging you want.
Second question, you can register a cancellation from anything that can access to your cancellationtokensource by calling cancel. So any event that is able to access the cancellationtokensource you could call it's cancellation event. I will often put a tokensource as an instance variable on a form that should support cancellation so that a "cancel" button or some other event that causes cancellation can call on the cancel method for the cts.
Example of one way I'll set up a form with a token:
public class MyForm
{
private CancellationTokenSource _cts;
private void Cancel()
{
if (_cts != null) {
_cts.Cancel();
}
}
}
Exactly when does WaitHandle WaitOne(int timeout) return? Does it return when the timeout has elapsed? I see some code online which suggests polling WaitOne() when implementing logic which does some cleanup before exiting. This implies that WaitOne() does not return when the timeout elapses; instead it returns whether or not it is signaled immediately after it is called.
public void SomeMethod()
{
while (!yourEvent.WaitOne(POLLING_INTERVAL))
{
if (IsShutdownRequested())
{
// Add code to end gracefully here.
}
}
// Your event was signaled so now we can proceed.
}
What I am trying to achieve here is a way to signal the WaitHandle using a CancellationToken while it is blocking the calling thread.
"I want to essentially stop blocking the calling thread while it is waiting even before the WaitHandle times out or is signaled" -- under what condition would you want the thread to become unblocked? Do you already have a CancellationToken object you're using?
If so, then you could do something like this:
public void SomeMethod(CancellationToken token)
{
int waitResult;
while ((waitResult = WaitHandle.WaitAny(
new [] { yourEvent, token.WaitHandle }, POLLING_INTERVAL)) == WaitHandle.WaitTimeout)
{
if (IsShutdownRequested())
{
// Add code to end gracefully here.
}
}
if (waitResult == 0)
{
// Your event was signaled so now we can proceed.
}
else if (waitResult == 1)
{
// The wait was cancelled via the token
}
}
Note that the use of WaitHandle is not necessarily ideal. .NET has modern, managed thread synchronization mechanisms that work more efficiently than WaitHandle (which is based on native OS objects that incur greater overhead). But if you must use WaitHandle to start with, the above is probably an appropriate way to extend your current implementation to work with CancellationToken.
If the above does not address your question, please improve the question by providing a good, minimal, complete code example that clearly illustrates the scenario, along with a detailed explanation of what that code example does now and how that's different from what you want it to do.
I have a Thread with this code:
foreach (string file in allDirectoriesFiles)
{
if (cancellationToken.IsCancellationRequested)
{
return;
}
// ... some operation...
}
// ... some operation 2...
notice that I use return instead of break, because if I stop the thread the some operation 2 must not be executed.
I also notice that in this manner the .ContinueWith it is not executed.
Well, but that is just a "return" function. How can C# know that I'm returning due to the tokenSource.Cancel() instead of a usual return?
You should not create the continuation with the same cancellation token or else the continuation will also be cancelled, even before it is executed. Create the continuation with a different cancellation token (from a different cancellation source) or simply create it without a cancellation token if you always want the cancellation to execute.
A CancellationToken is meant to propagate the cancelling to the whole set of actions at once. Once you Cancel() its source, the other continuations won't be called.
I have a task and I expect it to take under a second to run but if it takes longer than a few seconds I want to cancel the task.
For example:
Task t = new Task(() =>
{
while (true)
{
Thread.Sleep(500);
}
});
t.Start();
t.Wait(3000);
Notice that after 3000 milliseconds the wait expires. Was the task canceled when the timeout expired or is the task still running?
Task.Wait() waits up to specified period for task completion and returns whether the task completed in the specified amount of time (or earlier) or not. The task itself is not modified and does not rely on waiting.
Read nice series: Parallelism in .NET, Parallelism in .NET – Part 10, Cancellation in PLINQ and the Parallel class by Reed Copsey
And: .NET 4 Cancellation Framework / Parallel Programming: Task Cancellation
Check following code:
var cts = new CancellationTokenSource();
var newTask = Task.Factory.StartNew(state =>
{
var token = (CancellationToken)state;
while (!token.IsCancellationRequested)
{
}
token.ThrowIfCancellationRequested();
}, cts.Token, cts.Token);
if (!newTask.Wait(3000, cts.Token)) cts.Cancel();
If you want to cancel a Task, you should pass in a CancellationToken when you create the task. That will allow you to cancel the Task from the outside. You could tie cancellation to a timer if you want.
To create a Task with a Cancellation token see this example:
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var t = Task.Factory.StartNew(() => {
// do some work
if (token.IsCancellationRequested) {
// Clean up as needed here ....
}
token.ThrowIfCancellationRequested();
}, token);
To cancel the Task call Cancel() on the tokenSource.
The task is still running until you explicitly tell it to stop or your loop finishes (which will never happen).
You can check the return value of Wait to see this:
(from http://msdn.microsoft.com/en-us/library/dd235606.aspx)
Return Value
Type: System.Boolean
true if the Task completed execution within the allotted time; otherwise, false.
Was the task canceled when the timeout expired or is the task still running?
No and Yes.
The timeout passed to Task.Wait is for the Wait, not the task.
If your task calls any synchronous method that does any kind of I/O or other unspecified action that takes time, then there is no general way to "cancel" it.
Depending on how you try to "cancel" it, one of the following may happen:
The operation actually gets canceled and the resource it works on is in a stable state (You were lucky!)
The operation actually gets canceled and the resource it works on is in an inconsistent state (potentially causing all sorts of problems later)
The operation continues and potentially interferes with whatever your other code is doing (potentially causing all sorts of problems later)
The operation fails or causes your process to crash.
You don't know what happens, because it is undocumented
There are valid scenarios where you can and probably should cancel a task using one of the generic methods described in the other answers. But if you are here because you want to interrupt a specific synchronous method, better see the documentation of that method to find out if there is a way to interrupt it, if it has a "timeout" parameter, or if there is an interruptible variation of it.