Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Assuming that you don't want to call the <a href="http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.stop.aspx" rel="nofollow noreferrer"><code>Stop</code> method</a> on the <a href="http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.aspx" rel="nofollow noreferrer"><code>TcpListener</code> class</a>, there's no perfect solution here.</p> <p>If you're alright with being notified when the operation doesn't complete within a certain time frame, but allowing the original operation to complete, then you can create an extension method, like so:</p> <pre><code>public static async Task&lt;T&gt; WithWaitCancellation&lt;T&gt;( this Task&lt;T&gt; task, CancellationToken cancellationToken) { // The tasck completion source. var tcs = new TaskCompletionSource&lt;bool&gt;(); // Register with the cancellation token. using(cancellationToken.Register( s =&gt; ((TaskCompletionSource&lt;bool&gt;)s).TrySetResult(true), tcs) ) { // If the task waited on is the cancellation token... if (task != await Task.WhenAny(task, tcs.Task)) throw new OperationCanceledException(cancellationToken); } // Wait for one or the other to complete. return await task; } </code></pre> <p>The above is from <a href="http://blogs.msdn.com/b/pfxteam/archive/2012/10/05/how-do-i-cancel-non-cancelable-async-operations.aspx" rel="nofollow noreferrer">Stephen Toub's blog post "How do I cancel non-cancelable async operations?"</a>.</p> <p>The caveat here bears repeating, this doesn't actually <em>cancel</em> the operation, because there is not an overload of the <a href="http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.accepttcpclientasync.aspx" rel="nofollow noreferrer"><code>AcceptTcpClientAsync</code> method</a> that takes a <a href="http://msdn.microsoft.com/en-us/library/system.threading.cancellationtoken.aspx" rel="nofollow noreferrer"><code>CancellationToken</code></a>, it's not <em>able</em> to be cancelled.</p> <p>That means that if the extension method indicates that a cancellation <em>did</em> happen, you are cancelling the wait on the callback of the original <a href="http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.aspx" rel="nofollow noreferrer"><code>Task</code></a>, <em>not</em> cancelling the operation itself.</p> <p>To that end, that is why I've renamed the method from <code>WithCancellation</code> to <code>WithWaitCancellation</code> to indicate that you are cancelling the <em>wait</em>, not the actual action.</p> <p>From there, it's easy to use in your code:</p> <pre><code>// Create the listener. var tcpListener = new TcpListener(connection); // Start. tcpListener.Start(); // The CancellationToken. var cancellationToken = ...; // Have to wait on an OperationCanceledException // to see if it was cancelled. try { // Wait for the client, with the ability to cancel // the *wait*. var client = await tcpListener.AcceptTcpClientAsync(). WithWaitCancellation(cancellationToken); } catch (AggregateException ae) { // Async exceptions are wrapped in // an AggregateException, so you have to // look here as well. } catch (OperationCancelledException oce) { // The operation was cancelled, branch // code here. } </code></pre> <p>Note that you'll have to wrap the call for your client to capture the <a href="http://msdn.microsoft.com/en-us/library/system.operationcanceledexception.aspx" rel="nofollow noreferrer"><code>OperationCanceledException</code></a> instance thrown if the wait is cancelled.</p> <p>I've also thrown in an <a href="http://msdn.microsoft.com/en-us/library/system.aggregateexception.aspx" rel="nofollow noreferrer"><code>AggregateException</code></a> catch as exceptions are wrapped when thrown from asynchronous operations (you should test for yourself in this case).</p> <p>That leaves the question of which approach is a better approach in the face of having a method like the <a href="http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.stop.aspx" rel="nofollow noreferrer"><code>Stop</code> method</a> (basically, anything which violently tears everything down, regardless of what is going on), which of course, depends on your circumstances.</p> <p>If you are not sharing the resource that you're waiting on (in this case, the <code>TcpListener</code>), then it would probably be a better use of resources to call the abort method and swallow any exceptions that come from operations you're waiting on (you'll have to flip a bit when you call stop and monitor that bit in the other areas you're waiting on an operation). This adds some complexity to the code but if you're concerned about resource utilization and cleaning up as soon as possible, and this choice is available to you, then this is the way to go.</p> <p>If resource utilization is <em>not</em> an issue and you're comfortable with a more cooperative mechanism, and you're <em>not</em> sharing the resource, then using the <code>WithWaitCancellation</code> method is fine. The pros here are that it's cleaner code, and easier to maintain.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload