简体   繁体   English

.NET取消异步I / O操作

[英].NET cancelling async I/O operations

In this particular case, I'm writing a TCP/IP server, and I'm using TcpListener.BeginAcceptTcpClient() to accept incoming connections. 在这种特殊情况下,我正在编写TCP / IP服务器,而我正在使用TcpListener.BeginAcceptTcpClient()来接受传入连接。 The server logics is implemented in a single class implementing the IDisposable interface: when Dispose() is called, I want to cancel the BeginAcceptTcpClient() operation. 服务器逻辑在实现IDisposable接口的单个​​类中实现:当调用Dispose()时,我想取消BeginAcceptTcpClient()操作。

Now in more general terms: when using the async I/O model of .NET (Begin* and End*), how can I cancel the operations? 现在用更一般的术语来说:当使用.NET的异步I / O模型 (Begin *和End *)时,如何取消操作?

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(). Dispose()会将标志设置为true,并且还会调用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. 但是我记得已经读过,对于每个Begin *调用必须有匹配的End *调用或者会发生不好的事情。

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. 请注意,我正在寻找使用Begin *和End *方法取消的一般解决方案 - 我刚刚给出了具体的使用方案,以帮助您理解我的问题。

The general solution is to call Close() or Dispose() on whatever object whose BeginXxxx() method you called. 一般的解决方案是在你调用的BeginXxxx()方法的任何对象上调用Close()或Dispose()。 That will cause the callback to run, when you call EndXxxx() then you'll get an ObjectDisposedException. 这将导致回调运行,当您调用EndXxxx()时,您将获得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(). 对于TcpListener,使用Server.Dispose(),比调用Stop()更有效。

The alternative I would recommend is not using the async-Begin-End pattern but Tasks. 我建议的替代方法是不使用async-Begin-End模式,而是使用Tasks。

You can use one of the TaskFactory.FromAsync overloads to create a task from your Begin/End methods and use this. 您可以使用TaskFactory.FromAsync重载之一从Begin / End方法创建任务并使用它。 You can use this .ContinueWith overload to specify a continuation with your own CancellationToken to stop it. 您可以使用此.ContinueWith重载来指定具有您自己的CancellationToken的延续以停止它。 IMHO it's recommended to use Tasks whenever you can because the next version of C# will build upon Task with it's async/await support. 恕我直言,建议尽可能使用任务,因为下一版本的C#将构建在Task的async / await支持上。

Here is a nice article explaining the common patterns and Tasks 这是一篇很好的文章,解释了常见的模式和任务

REMARK: 备注:

In my original answer I said you can use .Dispose to kill the Task but this might cause serious problems, because you can only Dispose Tasks in the Completed state. 在我的原始答案中,我说你可以使用.Dispose来杀死任务但这可能会导致严重的问题,因为你只能在完成状态下处理任务。

The AsyncCallback is called when an async process completes. 异步过程完成时会调用AsyncCallback。 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: 通常,您将使用Begin / End模式,如下所示:

  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. 通常这是通过CancelAsync实现来完成的,该实现在内部提前停止任务。 In the case of TcpListener you can just call listener.Server.Dispose(); 对于TcpListener您只需调用listener.Server.Dispose();

 TcpListener listener = new TcpListener(port);
 IAsyncResult = listener.BeginAcceptTcpClient(listener.EndAcceptTcpClient, null);

 // on dispose
 listener.Server.Dispose();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM