C# blocking thread cancellation - c#

I am currently developing a service which basically waits for data on a stream. The service can be cancelled at any time, but in most cases it runs for a long period (days). I like to switch my asynchronous design from using BackgroundWorker and a loop which checks the current cancellation state of the BackgroundWorker and the availability of some data on stream to a much better design using Tasks (as the BackgroundWorker is not the best choice for long running threads, as it takes a ThreadPool-Thread).
My current loop / routine looks basically like this:
while(!cancelled) {
CheckIfDataAvailable(); // This check is non-blocking
Thread.Sleep(500);
}
What I basically want to do now is get rid of the Thread.Sleep() call, but in the same time the thread should be cancelable in a fair amount of time while also listening if any data is available on a stream (and this operation might block the stream or hinders the check of the cancelation state in some other way).
I know how to cancel a Task using a CancellationToken but I am not aware of any method combining the cancel-check and the data availability check in a really clean manner (e.g. not using Thread.Sleep()).

Use Task.Delay, e.g., as follows:
async Task BackgroundWord(CancellationToken token)
{
CheckIfDataAvailable();
await Task.Delay(TimeSpan.FromSeconds(x), token);
}
If your CheckIfDataAvailable method returns a Task (that is completed when data is available), you can combine the two as follows:
await Task.WhenAny(CheckIfDataAvailable(), Task.Delay(-1, token));
Task.Delay(-1) will wait forever, so it will only transfer to the completed state if the cancellation token is cancelled. Thus Task.WhenAny will wait for either data to become available, or the token to be cancelled.

Related

High performance async monitoring tasks

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().

What really happens when call async method?

I try to understand why is better using the 'Async' method than using simple old synchronous way.
There is small issue that I don't understand.
On the synchronous way:
I have some thread that call method FileStream.Read(...).
Because calling this method is synchronous so the calling thread will wait until the IRP (I/O request packet) will signal that this Io request is finish.
Until the IRP will return ==> this thread will suspend ( sleep ).
On the A-synchronous way:
I have some thread (Task .. lets call this thread 'TheadAsync01') that calls method FileStream.ReadAsync(...).
Because calling this method is A-Synchronous so the calling thread will not wait until the IRP (I/O request packet) will signal that this IO request is finish; and this calling thread will continue to his next action.
Now, When the IRP will signal that this IO request is finish what happened?
(The thread TheadAsync01 is now doing something else and can't continue the work with what the 'FileStream.ReadAsync' return now.)
Is other thread will continue the continue the next action with the return value of the ReadAsync?
What I don't understand here?
The reason it bothers you is this mistaken assumption:
The thread TheadAsync01 is now doing something else and can't continue
the work with what the 'FileStream.ReadAsync' return now.
In a typical application I/O is by far the most time-consuming task.
When TPL is used correctly, threads are not blocked by time-consuming operations. Instead, everything time-consuming (in other words, any I/O) is delegated via await. So when your IRP signals, the thread will either be free from work, or will be free very soon.
If there's some heavy calculation (something time-consuming which is not I/O), you need to plan accordingly, for example run it on a dedicated thread.
The function ReadAsync immediately returns a value, namely a Task object. Somewhere you should do something with the return value. The canonical way is to use await:
await FileStream.ReadAsync(...)
This will ensure that the calling site will not continue with operation until ReadAsync has completed its job. If you want to do something in the meantime you could await the task object later or you can manually deal with the task object.
If you just call ReadAsync, ignoring the returned task object, doing nothing with it, then your reading is mostly an expensive no-op.
When a ***Async method returns a Task or Task you use this to track the running of the asynchronous operation. You can make the call behave synchronously with respect to the calling code by calling .Wait() on the task. Alternatively, as of .Net 4.5 you can await the task.
e.g:
private async void DoFileRead(...)
{
var result = await fileStream.ReadAsync(...);
// Do follow on tasks
}
In this scenario any follow on code would be wrapped in a continuation by the compiler and executed when the async call completed. One requirement of using the async keyword is to mark the calling method with the async keyword (see the example above).

Is it considered good practice to use Task.ContinueWith() as a form of event "TaskEnded"

Task doesn't have a completed event/method that I can use to execute follow up code. If Task were to execute a long running operation (such as downloading data from the web to update a local database), is it considered good practice to use the method Task.ContinueWith() for executing an on completed like event? Are there any unwanted side effects or issues I may run into with this approach?
It is fine to use ContinueWith() to follow up on completion of long-running operations. However, as of .NET 4.5 there is a more concise way to write this, which is to use the async/await keywords. For instance:
using (var client = new HttpClient(...))
{
// long-running download operation, but UI remains responsive because
// the operation executes asynchronously
var response = await client.GetAsync();
// control resumes here once the above completes,
// returning control to the UI thread.
this.TextField.Text = "Download Complete!";
}
You can interpret whatever happens after the await as the continuation, i.e. the stuff you would normally put in your ContinueWith(). The await has the effect of waiting for the operation to complete, unwrapping the result from the returned task, and resuming execution on the current context - in this case the UI thread.
This is a great way to carry out long-running I/O operations and still have a responsive UI. You will need to do this in an async method - see the official documentation for more information.

Task.Factory.FromAsync with CancellationTokenSource

I have the following line of code used to read asynchronously from a NetworkStream:
int bytesRead = await Task<int>.Factory.FromAsync(this.stream.BeginRead, this.stream.EndRead, buffer, 0, buffer.Length, null);
I'd like to make it support cancellation. I see that I can cancel tasks using a CancellationTokenSource, however I don't see any way I can pass it to TaskFactory.FromAsync().
Is it possible to make a FromAsync()-constructed task support cancellation?
Edit: I want to cancel a task that is already running.
Gigi, unfortunately the semantic nature of FromAsync indicates that you are only adapting an asynchronous process to TPL's API (TPL = Microsoft's Task Parallel Library)
In essence, TPL's ReadAsync controls the async behaviour itself, whilst FromAsync only wraps the behaviour (but doesn't control it).
Now since Cancellation is a TPL specific construct, and since FromAsync has no control on the inner workings of the async method being called, then there is no guaranteed way to cleanly cancel the task and ensure that all resources are closed correctly (which is why it was omitted. If you're curious, just decompile the method ;))
In these situations, it makes more sense to wrap the actual async call yourself in a normal task and detect the OperationCancelled exception, which will give you the opportunity to close your stream by making the appropriate calls.
In short, the answer is no, but there is nothing stopping you from creating a generic overloaded method that will pick the correct strategy to cleanly close a stream depending on its type.
As others have already mentioned, there is no clean way of achieving what you're asking for. The notion of cancellation was absent from the Asynchronous Programming Model; thus, it couldn't be retrofitted through the FromAsync converters.
However, you can introduce cancellation for the Task that wraps the asynchronous operation. This will not cancel the underlying operation itself – your NetworkStream would still continue reading all the requested bytes from the socket – but it will permit your application to react as if the operation was cancelled, immediately throwing an OperationCanceledException from your await (and executing any registered task continuations). The result of the underlying operation, once completed, will be ignored.
This is a helper extension method:
public static class TaskExtensions
{
public async static Task<TResult> HandleCancellation<TResult>(
this Task<TResult> asyncTask,
CancellationToken cancellationToken)
{
// Create another task that completes as soon as cancellation is requested.
// http://stackoverflow.com/a/18672893/1149773
var tcs = new TaskCompletionSource<TResult>();
cancellationToken.Register(() =>
tcs.TrySetCanceled(), useSynchronizationContext: false);
var cancellationTask = tcs.Task;
// Create a task that completes when either the async operation completes,
// or cancellation is requested.
var readyTask = await Task.WhenAny(asyncTask, cancellationTask);
// In case of cancellation, register a continuation to observe any unhandled
// exceptions from the asynchronous operation (once it completes).
// In .NET 4.0, unobserved task exceptions would terminate the process.
if (readyTask == cancellationTask)
asyncTask.ContinueWith(_ => asyncTask.Exception,
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously);
return await readyTask;
}
}
And this is an example that uses the extension method to treat an operation as cancelled after 300ms:
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromMilliseconds(300));
try
{
int bytesRead =
await Task<int>.Factory.FromAsync(this.stream.BeginRead, this.stream.EndRead, buffer, 0, buffer.Length, null)
.HandleCancellation(cts.Token);
}
catch (OperationCanceledException)
{
// Operation took longer than 300ms, and was treated as cancelled.
}
No, there is no generic way to cancel such a task. Cancellation is API specific.
For example, WebClient has a Cancel method.
A Socket or a FileStream needs to be Close'd to cancel an outstanding call.
Web-service clients have even different ways of aborting calls.
...
This is because the implementer of the IO operation must support cancellation.
It might seem tempting to use NetworkStream.ReadAsync and pass a cancellation token but is Stream.ReadAsync. An the latter just throws away the token. Basically not supported.
Stream.ReadAsync is just the base class method. It does not do anything by itself. Concrete IO operations are issued only by derived classed. Those must support cancellation natively. Stream can't do anything to force them. It happens that NetworkStream doesn't support cancellation.
I understand that you want to cancel the operation and leave the socket open. But it is not possible. (Subjective note: This is really a sad state of affairs. Especially considering that Windows supports cancellable IO at the Win32 level.)
If you still want your app to quickly continue, although the IO operation is not cancellable, just ignore the result of that task and continue. Be aware that eventually the IO might complete and for example drain data from the socket buffers or cause other side-effects.
"Cancelling by ignoring" effectively make the stream position undefined. The stream becomes unusable. This doesn't really avoid the need to open a new stream. You still have to get rid of the old stream (in most cases) and reopen. Also, you are introducing concurrency.

Cancelling a Thread due to hung Db call

I've designed and made a prototype application for a high performance, multi-threaded mail merge to run as a Windows Service (C#). This question refers to one sticky part of the problem, what to do if the process hangs on a database call. I have researched this a lot. I have read a lot of articles about thread cancellation and I ultimately only see one way to do this, thread.Abort(). Yes, I know, absolutely do not use Thread.Abort(), so I have been researching for days how to do it another way and as I see it, there is no alternative. I will tell you why and hopefully you can tell me why I am wrong.
FYI, these are meant as long running threads, so the TPL would make them outside the ThreadPool anyway.
TPL is just a nice wrapper for a Thread, so I see absolutely nothing a Task can do that a Thread cannot. It's just done differently.
Using a thread, you have two choices for stopping it.
1. Have the thread poll in a processing loop to see if a flag has requested cancellation and just end the processing and let the thread die. No problem.
2. Call Thread.Abort() (then catch the exception, do a Join and worry about Finally, etc.)
This is a database call in the thread, so polling will not work once it is started.
On the other hand, if you use TPL and a CancellationToken, it seems to me that you're still polling and then creating an exception. It looks like the same thing I described in case 1 with the thread. Once I start that database call (I also intend to put a async / await around it), there is no way I can test for a change in the CancellationToken. For that matter, the TPL is worse as calling the CancellationToken during a Db read will do exactly nothing, far less than a Thread.Abort() would do.
I cannot believe this is a unique problem, but I have not found a real solution and I have read a lot. Whether a Thread or Task, the worker thread has to poll to know it should stop and then stop (not possible when connected to a Db. It's not in a loop.) or else the thread must be aborted, throwing a ThreadAbortedException or a TaskCanceledException.
My current plan is to start each job as a longrunning thread. If the thread exceeds the time limit, I will call Thread.Abort, catch the exception in the thread and then do a Join() on the thread after the Abort().
I am very, very open to suggestions... Thanks, Mike
I will put this link, because it claims to do this, but I'm having trouble figuring it out and there are no replys to make me think it will work
multi-threading-cross-class-cancellation-with-tpl
Oh, this looked like a good possibility, but I don't know about it either Treating a Thread as a Service
You can't actually cancel the DB operation. The request is sent across the network; it's "out there" now, there's no pulling it back. The best you can really do is ignore the response that comes back, and continue on executing whatever code you would have executed had the operation actually completed. It's important to recognize what this is though; this isn't actually cancelling anything, it's just moving on even though you're not done. It's a very important distinction.
If you have some task, and you want it to instead become cancelled when you want it to be, you can create a continuation that uses a CancellationToken such that the continuation will be marked as canceled when the token indicates it should be, or it'll be completed when the task completes. You can then use that continuation's Task in place of the actual underlying tasks for all of your continuations, and the task will be cancelled if the token is cancelled.
public static Task WithCancellation(this Task task
, CancellationToken token)
{
return task.ContinueWith(t => t.GetAwaiter().GetResult(), token);
}
public static Task<T> WithCancellation<T>(this Task<T> task
, CancellationToken token)
{
return task.ContinueWith(t => t.GetAwaiter().GetResult(), token);
}
You can then take a given task, pass in a cancellation token, and get back a task that will have the same result except with altered cancellation semantics.
You have several other options for your thread cancellation. For example, your thread could make an asynchronous database call and then wait on that and on the cancellation token. For example:
// cmd is a SqlCommand object
// token is a cancellation token
IAsyncResult ia = cmd.BeginExecuteNonQuery(); // starts an async request
WaitHandle[] handles = new WaitHandle[]{token.WaitHandle, ia.AsyncWaitHandle};
var ix = WaitHandle.WaitAny(handles);
if (ix == 0)
{
// cancellation was requested
}
else if (ix == 1)
{
// async database operation is done. Harvest the result.
}
There's no need to throw an exception if the operation was canceled. And there's no need for Thread.Abort.
This all becomes much cleaner with Task, but it's essentially the same thing. Task handles common errors and helps you to do a better job fitting all the pieces together.
You said:
TPL is just a nice wrapper for a Thread, so I see absolutely nothing a Task can do that a Thread cannot. It's just done differently.
That's true, as far as it goes. After all, C# is just a nice wrapper for an assembly language program, so I see absolutely nothing a C# program can do that I can't do in assembly language. But it's a whole lot easier and faster to do it with C#.
Same goes for the difference between TPL or Tasks, and managing your own threads. You can do all manner of stuff managing your own threads, or you can let the TPL handle all the details and be more likely to get it right.

Categories

Resources