首先,您应该将返回类型更改为 Task,而不是 void。async void
方法本质上是一劳永逸的,不能等待或取消。它们的存在主要是为了允许创建异步事件处理程序或类似事件的代码。它们不应该用于正常的异步操作。
协同取消/中止/停止异步操作的 TPL 方法是使用CancellationToken。您可以检查令牌的IsCancellationRequested属性以查看是否需要取消操作并停止。
更好的是,框架提供的大多数异步方法都接受 CancellationToken,因此您可以立即停止它们而无需等待它们返回。您可以使用 NetworkStream 的ReadAsync(Byte[], Int32, Int32, CancellationToken)读取数据并在有人调用您的 Stop 方法时立即取消。
您可以将代码更改为以下内容:
CancellationTokenSource _source;
public void Start()
{
_source = new CancellationTokenSource();
Task.Factory.StartNew(() => Listen(1, _source.Token),_source.Token);
Task.Factory.StartNew(() => Listen(2, _source.Token), _source.Token);
}
public void Stop()
{
_source.Cancel();
}
private async Task Listen(int port,CancellationToken token)
{
var tcp = new TcpClient();
while(!token.IsCancellationRequested)
{
await tcp.ConnectAsync(ip, port);
using (var stream=tcp.GetStream())
{
...
try
{
await stream.ReadAsync(buffer, offset, count, token);
}
catch (OperationCanceledException ex)
{
//Handle Cancellation
}
...
}
}
}
您可以在托管线程中的取消中阅读更多关于取消的信息,包括有关如何轮询、注册取消回调、侦听多个令牌等的建议。
该try/catch
块之所以存在,是因为await
如果取消任务,则会引发异常。您可以通过对 ReadAsync 返回的任务调用 ContinueWith 并检查 IsCanceled 标志来避免这种情况:
private async Task Listen(int port,CancellationToken token)
{
var tcp = new TcpClient();
while(!token.IsCancellationRequested)
{
await tcp.ConnectAsync(ip, port);
using (var stream=tcp.GetStream())
{
///...
await stream.ReadAsync(buffer, offset, count, token)
.ContinueWith(t =>
{
if (t.IsCanceled)
{
//Do some cleanup?
}
else
{
//Process the buffer and send notifications
}
});
///...
}
}
}
await
现在等待一个简单Task
的在延续完成时完成