In this particular case, I'm writing a TCP/IP server, and I'm using TcpListener.BeginAcceptTcpClient() to accept incoming connections. The server logics is implemented in a single class implementing the IDisposable interface: when Dispose() is called, I want to cancel the BeginAcceptTcpClient() operation.
Now in more general terms: when using the async I/O model of .NET (Begin* and End*), how can I cancel the operations?
My idea would be something like this:
private volatile bool canceledFlag = false;
this.listener.BeginAcceptTcpClient(asyncResult =>
{
if(this.canceledFlag) return;
// ...
}, null);
Dispose() would set the flag to true and would also call this.listener.Stop(). However I recall having read that for every Begin* call there must be a matching End* call or bad things would happen.
So how should I do this then? Please note that I'm looking for a general solution for cancellation with Begin* and End* methods - I just gave you my concrete usage scenario to help you understand my question.
The general solution is to call Close() or Dispose() on whatever object whose BeginXxxx() method you called. That will cause the callback to run, when you call EndXxxx() then you'll get an ObjectDisposedException. Which you should catch and treat as a 'end of use' signal, clean up and exit the callback right away. While frowned upon, the use of an exception for flow control is unavoidable.
For TcpListener, use Server.Dispose(), a bit more efficient than calling Stop().
The AsyncCallback is called when an async process completes. It allows the async thread to rejoin the original, and any exceptions can then bubble up (rather than being lost in the background). It also allows you to capture any return values.
Typically you would use Begin / End pattern as follows:
Action action = () = > DoSomething();
action.BeginInvoke(action.EndInvoke, null);
Or
Action action = () = > DoSomething();
action.BeginInvoke(OnComplete, null);
private void OnComplete(IAsyncResult ar)
{
AsyncResult result = (AsyncResult)ar;
var caller = (Action)result.AsyncDelegate;
caller.EndInvoke();
// do something else when completed
}
It's up to specific implementations to provide a way to cancel an async process before completion. Usually this is done with a CancelAsync
implementation that internally stops the task early. In the case of TcpListener
you can just call listener.Server.Dispose();
TcpListener listener = new TcpListener(port);
IAsyncResult = listener.BeginAcceptTcpClient(listener.EndAcceptTcpClient, null);
// on dispose
listener.Server.Dispose();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With