在现代应用程序开发中,异步编程已成为处理并发操作的一种高效方式。它不仅提高了应用程序的响应性,还简化了代码的编写和维护。本文将探讨如何使用.NET的异步编程模式(Task-based Asynchronous Pattern,简称TAP)和进度报告接口(IProgress)来创建一个异步的Ping工具。
.NET的异步编程模式(TAP)定义了异步函数应该实现的接口。它包括两个部分:编译器的扩展和基于任务的异步模式。编译器扩展通过引入async和await关键字来实现,而TAP则定义了异步函数的签名。
异步函数的签名如下:
public Task<TResult> DoWorkAsync(… parameters, CancellationToken cancellationToken, IProgress<T> progress);
其中,方法返回一个Task,方法名后附加了Async,方法接受一个CancellationToken和一个IProgress<T>。
.NET Framework 4.5引入了IProgress<T>接口,它包含一个Report方法,用于异步方法报告进度。Progress<T>类是IProgress<T>的一个简单实现,它提供了一个接受Action<T>的构造函数和一个ProgressChanged事件,用于注册进度更新的回调。
为了实现异步Ping,首先需要一个类来报告Ping的进度。这个类是一个容器,用于存储Ping状态的各种信息。
public class PingResult
{
public IPAddress Address { get; private set; }
public int PingsTotal { get; private set; }
public int PingsSuccessful { get; private set; }
public TimeSpan AverageTime { get; private set; }
public TimeSpan LastTime { get; private set; }
public IPStatus LastStatus { get; private set; }
public PingResult(IPAddress address)
{
Address = address;
LastStatus = IPStatus.Unknown;
}
internal void AddResult(PingReply res) { ... }
}
PingResult类包含了Ping操作的各种状态信息,如地址、总Ping次数、成功Ping次数、平均时间、最后一次Ping的时间和状态等。AddResult方法将由Ping方法调用,以累积信息到AverageTime、PingsTotal等属性中。
有了PingResult类,就可以创建一个异步Ping方法了。这个方法是对System.Net.NetworkInformation.Ping功能的包装。
public static async Task ContinuousPingAsync(
IPAddress address,
int timeout,
int delayBetweenPings,
IProgress<PingResult> progress,
CancellationToken cancellationToken)
{
var ping = new System.Net.NetworkInformation.Ping();
var result = new PingResult(address);
while (!cancellationToken.IsCancellationRequested)
{
var res = await ping.SendPingAsync(address, timeout).ConfigureAwait(false);
result.AddResult(res);
progress.Report(result);
await Task.Delay(delayBetweenPings).ConfigureAwait(false);
}
}
这个方法是一个静态方法,所有状态(result和ping)都隐藏在内部。方法返回一个Task,以便调用者可以处理运行中的计算。IProgress<PingResult>对象用于报告Ping状态。方法可以使用CancellationToken来取消。
源代码还包括一个简单的WPF MVVM应用程序示例。可以输入要Ping的计算机地址。DNS解析和Ping都是使用本文的方法异步完成的。
namespace Network
{
public static class Ping
{
public static IPAddress ResolveAddress(string hostNameOrAddress) { ... }
public static async Task<IPAddress> ResolveAddressAsync(string hostNameOrAddress) { ... }
public static async Task ContinuousPingAsync(
IPAddress address,
int timeout,
int delayBetweenPings,
IProgress<PingResult> progress,
CancellationToken cancellationToken) { ... }
}
}