在这种特殊情况下,我正在编写TCP / IP服务器,而我正在使用TcpListener.BeginAcceptTcpClient()来接受传入连接。服务器逻辑在实现IDisposable接口的单个类中实现:当调用Dispose()时,我想取消BeginAcceptTcpClient()操作。
现在用更一般的术语来说:当使用.NET的异步I / O模型(Begin *和End *)时,如何取消操作?
我的想法是这样的:
private volatile bool canceledFlag = false;
this.listener.BeginAcceptTcpClient(asyncResult =>
{
if(this.canceledFlag) return;
// ...
}, null);
Dispose()会将标志设置为true,并且还会调用this.listener.Stop()。 但是我记得已经读过,每次开始*调用必须有一个匹配的结束*调用或者会发生不好的事情。
那我该怎么做呢?请注意,我正在寻找一个通用解决方案来取消使用Begin *和End *方法 - 我只是给了我具体的使用方案,以帮助您理解我的问题。
答案 0 :(得分:7)
通用解决方案是在您调用其BeginXxxx()方法的任何对象上调用Close()或Dispose()。这将导致回调运行,当您调用EndXxxx()时,您将获得ObjectDisposedException。您应该将其视为“使用结束”信号,立即清理并退出回调。虽然不赞成使用流量控制的例外是不可避免的。
对于TcpListener,使用Server.Dispose(),比调用Stop()更有效。
答案 1 :(得分:1)
我建议的替代方法是不使用async-Begin-End模式,而是使用Tasks。
您可以使用其中一个TaskFactory.FromAsync重载从Begin / End方法创建任务并使用它。您可以使用此.ContinueWith重载来指定具有自己的CancellationToken的延续以停止它。恕我直言,建议尽可能使用任务,因为下一版本的C#将构建在任务上,并且它具有async / await支持。
这是一个很好的article,解释了常见的模式和任务
REMARK:
在我原来的回答中,我说你可以使用.Dispose来杀死任务但这可能会导致严重的问题,因为你只能在完成状态下处理任务。
答案 2 :(得分:1)
异步过程完成时会调用AsyncCallback。它允许异步线程重新加入原始,然后任何异常都可以冒泡(而不是在后台丢失)。它还允许您捕获任何返回值。
通常,您将使用开始/结束模式,如下所示:
Action action = () = > DoSomething();
action.BeginInvoke(action.EndInvoke, null);
或
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
}
由特定实现提供一种在完成之前取消异步进程的方法。通常这是通过CancelAsync
实现来完成的,该实现在内部提前停止任务。如果是TcpListener
,您只需拨打listener.Server.Dispose();
TcpListener listener = new TcpListener(port);
IAsyncResult = listener.BeginAcceptTcpClient(listener.EndAcceptTcpClient, null);
// on dispose
listener.Server.Dispose();