I've read Stephen Toub's blog about making a custom awaitable for SocketAsyncEventArgs. This works all fine. But what I need is a cancellable awaitable and the blog doesn't cover this topic. Also Stephen Cleary unfortunately doesn't cover in his book how to cancel async methods that don't support cancellation.
I tried to implement it myself, but I fail with the TaskCompletionSource and Task.WhenAny because with the awaitable I'm not actually awaiting a task.
This is what I would like to have: being able to use Socket's ConnectAsync with TAP format and being able to cancel it, while still having the reusability of SocketAsyncEventArgs.
public async Task ConnectAsync(SocketAsyncEventArgs args, CancellationToken ct) {}
And this is what I have from Stephen Toub's blog so far (and the SocketAwaitable implementation):
public static SocketAwaitable ConnectAsync(this Socket socket,
SocketAwaitable awaitable)
{
awaitable.Reset();
if (!socket.ConnectAsync(awaitable.m_eventArgs))
awaitable.m_wasCompleted = true;
return awaitable;
}
I just can't figure out how to get this into the TAP format and making it cancellable.
Any help is appreciated.
Edit 1:
This is the example how I would do cancellation normally:
private static async Task<bool> ConnectAsync(SocketAsyncEventArgs args, CancellationToken cancellationToken)
{
var taskCompletionSource = new TaskCompletionSource<bool>();
cancellationToken.Register(() =>
{TaskCompletionSource.Task
taskCompletionSource.TrySetCanceled();
});
// This extension method of Socket not implemented yet
var task = _socket.ConnectAsync(args);
var completedTask = await Task.WhenAny(task, taskCompletionSource.Task);
return await completedTask;
}
custom awaitable for SocketAsyncEventArgs.
This is doable, but SocketAsyncEventArgs is specifically for extremely performance-sensitive scenarios. The vast majority (and I mean >99.9%) of projects do not need it and can use TAP instead. TAP is nice because it interoperates well with other techniques... like cancellation.
what I need is a cancellable awaitable and the blog doesn't cover this topic. Also Stephen Cleary unfortunately doesn't cover in his book how to cancel async methods that don't support cancellation.
So, there's a couple of things here. Starting with "how to cancel async methods that don't support cancellation": the short answer is that you can't. This is because cancellation is cooperative; so if one side can't cooperate, it's just not possible. The longer answer is that it's possible but usually more trouble than it's worth.
In the general case of "cancel an uncancelable method", you can run the method synchronously on a separate thread and then abort that thread; this is the most efficient and most dangerous approach, since aborted threads can easily corrupt application state. To avoid this serious corruption problem, the most reliable approach is to run the method in a separate process, which can be terminated cleanly. However, that's usually overkill and way more trouble than it's worth.
Enough of the general answer; for sockets specifically, you cannot cancel a single operation, because this leaves the socket in an unknown state. Take the Connect example in your question: there's a race condition between the connection and the cancel. Your code might end up with a connected socket after it requests cancellation, and you don't want that resource sticking around. So the normal and accepted way to cancel a socket connection is to close the socket.
The same goes for any other socket operation: if you need to abort a read or write, then when your code issues a cancel, it can't know how much of the read/write completed - and that would leave the socket in an unknown state with regard to the protocol you're using. The proper way to "cancel" any socket operation is to close the socket.
Note that this "cancel" is at a different scope than the kind of cancellation we normally see. CancellationToken and friends represent the cancellation of a single operation; closing a socket cancels all operations for that socket. For this reason, the socket APIs do not take CancellationToken parameters.
Stephen Cleary unfortunately doesn't cover in his book how to cancel async methods that don't support cancellation. I tried to implement it myself
The question "how do I cancel an uncancelable method" is almost never properly solved with "use this code block". Specifically, the approach you used cancels the await, not the operation. After a cancel is requested, your application is still trying to connect to the server, and may eventually succeed or fail (both results would be ignored).
In my AsyncEx library, I do have a couple of WaitAsync overloads, which allow you to do something like this:
private static async Task MyMethodAsync(CancellationToken cancellationToken)
{
var connectTask = _socket.ConnectAsync(_host);
await connectTask.WaitAsync(cancellationToken);
...
}
I prefer this kind of API because it's clear that it's the asynchronous wait that is cancelled, not the connection operation.
I fail with the TaskCompletionSource and Task.WhenAny because with the awaitable I'm not actually awaiting a task.
Well, to do something more complex like this, you'll need to convert that awaitable to a Task. There may be a more efficient option, but that's really getting into the weeds. And even if you figure out all the particulars, you'll still end up with "fake cancelling": an API that looks like it cancels the operation but it really doesn't - it just cancels the await.
This is what I would like to have: being able to use Socket's ConnectAsync with TAP format and being able to cancel it, while still having the reusability of SocketAsyncEventArgs.
TL;DR: You should close the socket instead of cancelling a connection operation. That's the cleanest approach in terms of reclaiming resources quickly.
Related
I am trying to migrate to using the CancellationToken/Src constructs in .NET, e.g.: https://learn.microsoft.com/en-us/dotnet/standard/threading/cancellation-in-managed-threads
My basic question is in regards to reacting to a cancellation request.
In my case I have some long-running processing that is not important for consistency. When a cancellation request arrives, I can't afford the pattern of "polling", e.g.:
while (!_token.IsCancellationRequested)
{
DoProcessing(...)
}
Because the processing can take several minutes and I really want to exit right now.
My question is if the proper method is then to simply use this setup:
public void Start()
{
_token.Register(() => _token.ThrowIfCancellationRequested());
// Continuously process stuff.
while (!_token.IsCancellationRequested)
{
DoProcessing(...)
}
}
That is, register a callback which in turn just throws the OperationCanceledException.
I can read here: https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource.cancel?view=netframework-4.8 , that:
We recommend that cancelable operations and callbacks registered with CancellationToken not throw exceptions.
However...
What is the proper way of interrupting my processing immediately, rather than polling, and still adhering to the "rules" prescribed by the framework?
I can understand how that quote appears to recommend against using ThrowIfCancellationRequested, but that is not the case--using ThrowIfCancellationRequested is appropriate.
A couple of sources that indicate using ThrowIfCancellationRequested is appropriate:
This SO answer by Jon Skeet indicates that ThrowIfCancellationRequested is the correct approach.
Throwing OperationCanceledException is the idiomatic way that "the method you called was cancelled" is expressed in TPL. Don't fight against that - just expect it.
This Microsoft source
indicates that ThrowIfCancellationRequested is the correct approach.
A successful cancellation involves the requesting code calling the CancellationTokenSource.Cancel method, and the user delegate terminating the operation in a timely manner. You can terminate the operation by using one of these options:
By simply returning from the delegate [snipped remainder of this bullet]
By throwing a OperationCanceledException and passing it the token on which cancellation was requested. The preferred way to do this is to use the ThrowIfCancellationRequested method. A task that is canceled in this way transitions to the Canceled state, which the calling code can use to verify that the task responded to its cancellation request
There is also a code example demonstrating how to use ThrowIfCancellationRequested appropriately.
Another quote reading further into this Microsoft source:
When a task instance observes an OperationCanceledException thrown by user code ... the task interprets this as acknowledging cancellation and transitions to the Canceled state.
...
Also note that the presence of other exceptions will also cause the Task to transition to the Faulted state ...
I suspect (and this is just me attempting to reconcile the two sources) that this is what the recommendation you quoted is referring to--it's a recommendation to avoid disrupting the cancellation mechanism's ability to accurately indicate the task's state as canceled or faulted
I've got method which I want to run asynchronously:
public async Task Send(MemoryMapModel memoryMap, SerialPort serialPort, ProgressBar progressBar, Settings settings)
{
SendFrames(memoryMap, serialPort, progressBar, _token.Token, settings);
}
And I use it like this by Task.Run():
await Task.Run(async () => await _rtuLogic.Send(memoryMap, serialPort, pbProgressBar, settings).ConfigureAwait(false));
Method "Send" throws exceptions which I want to catch, but to do that, it has to be async Task, but there are no awaitable methods and VS wants me not to use async keyword.
Why can't I catch exceptions from Task.Run(() => void) but it do works for Task.Run(async () => async Task), though there are no awaitable methods?
EDIT:
Also when I tried
Task.Factory.StartNew(() => { _rtuLogic.SendAsync(memoryMap, serialPort, pbProgressBar, settings); });
I can't catch and handle exceptions.
If I don't use Task.Run(...) Send doesn't run asynchronously and freezes GUI.
Some people may see it as a bit outdated now we have async, but you seem to be throwing async/await in everywhere and hoping it'll solve your problem.1
So I'd suggest ditching this attempt and look at using BackgroundWorker instead. It lets you clearly separate the bits that should happen "elsewhere" from the bits that still need to access the UI and should help avoid blocking it.
Task.Run should generally be avoided unless you have CPU bound work that you want to move off the current thread.
If you do want to go async/await then ideally you find the async variant of SendFrames and use that instead. But if that's not available/written yet then don't try to pretend that it's async.
1A now deleted answer linked to this answer by Stephen Cleary which in turn links off to other articles/posts about async, which I'd suggest you should try to ready all of them and other Stephen Cleary blog posts too (There is no thread should always be a recommended read)
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.
What is the correct method to implement a infinitely running task which executes an Async BigJob()? and can be stopped upon request
Hint: I am trying to learn [a] new method(s) to update my existing strategy.
I have this simple program (a tester) which has an Start and Stop button.
When Start pressed, I am starting the tester which is going to find and test available devices in range and test the hell our of them in infinite amount of rounds until the user presses the Stop button.
The important point is, the main process/action/bigJob() is and awaitble asynchronous process.
So I would pseudo code the whole thing as
Start
Round 1 starts
Async Main job starts
Async connect
Async Read
Async Write
Async disconnect
Nobody cancelled me yet
Round 1 finishes
Round 2 starts
.
.
.
Round 2 finishes
Round 3 starts
.
Stop pressed, hence break out.
Stop
So I am using BackgroundWorker to implement the infinite loop and Async/Await for the Connect, read, write and disconnect that I have written in C# under .Net 4.5
Since my sub-tasks such as Connect and .etc are async then my main task is async and pressing the Stop button stops my process since it eliminates the infinite while loop that I have with having
while (!bw.CancellationPending)
{
await MainTask();
...
}
but it doesn't fire the BackgroundWorker_RunWorkerCompleted event, which doesn't do me any harm HOWEVER it's killing me since the background worker doesn't work as it's supposed to and I just keep thinking "there should be a better way!".
So, I have been reading about so many new ideas, like why to use BackgroundWorker at all now that we have Async/await. Some were saying use Task.Run() it's magical. I even read recommendations and blogs about this godsend TPL Dataflow that I have never heard of. And so far I haven't found a method which is as structured, documented and layed-out as a BackgroundWorker. (unless it's such simple or a one liner which doesn't even need documentation or .etc) So, guys, what is the solution for this. Or better way to ask:
What is the correct method to implement a infinitely running task which executes an Async BigJob()? and can be stopped upon request
You should use CancellationTokenSource. But don't pass the Token to the Task.Run method and don't throw an exception. Simply check if cancellation was requested and then stop.
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
Task.Run(
async () =>
{
while (!ct.IsCancellationRequested)
{
await ConnectAsync();
await ReadAsync();
await WriteAsync();
await DisconnectAsync();
}
});
cts.Cancel();
When you pass a CancellationToken to the Task itself(Task.Run(async () => ..., ct)), you can't use it inside the delegate. It will only cancel the task if cancellation was called before the task started running. After it has begun, calling Cancel won't have any effect.
Create a CancellationTokenSource and pass its Token to the async task. This will allow you to signal to your async tasks that cancellation is requested. More info: http://msdn.microsoft.com/en-us/library/hh873177(v=vs.110).aspx
I would strongly recommend everybody to have a read about three different methods of Asynchronous Programming Patterns and the use of the recent and preferred method Task-based Asynchronous Pattern (TAP)
Asynchronous Programming Model (APM)
Describes the legacy model that uses the IAsyncResult interface to
provide asynchronous behavior. This model is no longer recommended for
new development.
Event-based Asynchronous Pattern (EAP)
Describes the event-based legacy model for providing asynchronous
behavior. This model is no longer recommended for new development.
Task-based Asynchronous Pattern (TAP)
Describes the new asynchronous pattern based on the
System.Threading.Tasks namespace. This model is the recommended
approach to asynchronous programming in the .NET Framework 4 and later
versions.
Thanks to #SKall for pointing me to the right direction.
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.