我正在开发一个“心跳”应用程序,通过循环每分钟ping数百个IP地址。这些IP地址存储在一个名为Machines
的类列表中。我有一个循环来创建Task<MachinePingResults>
(其中MachinePingResults
基本上是IP和在线状态的元组),并调用使用System.Net.NetworkInformation
的ping函数。
问题是,在运行数小时(或数天)后,主程序的某个循环无法完成Tasks
,从而导致内存泄漏。我无法确定为什么我的任务没有完成(如果我在运行时查看任务列表,经过几天运行后,有数百个任务显示为“等待”)。大多数情况下,所有任务都会完成并被处理;只是偶尔它们不会完成。例如,过去24小时中有一个问题,大约在12小时时出现了148个未完成的等待任务。由于无法查看Ping
挂起的原因(因为它是.NET内部的),我无法重现此问题进行调试。
(据显示,如果存在DNS问题,则.NET中的Ping
调用可能会挂起,并且内置超时会失败,这就是为什么我构建了一个额外的超时)
如果ping在15秒内没有返回,我有一种方法可以取消主循环,使用Task.Delay
和CancellationToken
。然后,在每个Ping函数中,我都有一个Delay
,以防Ping调用本身挂起导致函数强制完成。还要注意,我只ping IPv4;没有IPv6或URL。
主循环
pingcancel = new CancellationTokenSource();
List<Task<MachinePingResults>> results = new List<Task<MachinePingResults>>();
try
{
foreach (var m in localMachines.FindAll(m => !m.Online))
results.Add(Task.Run(() =>
PingMachine(m.ipAddress, 8000), pingcancel.Token
));
await Task.WhenAny(Task.WhenAll(results.ToArray()), Task.Delay(15000));
pingcancel.Cancel();
}
catch (Exception ex) { Console.WriteLine(ex); }
finally
{
results.Where(r => r.IsCompleted).ToList()
.ForEach(r =>
//modify the online machines);
results.Where(r => r.IsCompleted).ToList().ForEach(r => r.Dispose());
results.Clear();
}
ping功能
static async Task<MachinePingResults> PingMachine(string ipaddress, int timeout)
{
try
{
using (Ping ping = new Ping())
{
var reply = ping.SendPingAsync(ipaddress, timeout);
await Task.WhenAny(Task.Delay(timeout), reply);
if (reply.IsCompleted && reply.Result.Status == IPStatus.Success)
{
return new MachinePingResults(ipaddress, true);
}
}
}
catch (Exception ex)
{
Debug.WriteLine("Error: " + ex.Message);
}
return new MachinePingResults(ipaddress, false);
}
每个 Task
都有一个延迟以便在 Ping 挂起时继续执行,但我不知道是什么原因导致一些 Task<MachinePingResults>
永远无法完成。
如何确保使用 .NET Ping
的 Task
结束?
使用 .NET 5.0,在运行 Windows 10 和 Windows Server 2012 的机器上出现问题。
Ping
抛出异常,它会挂起函数和任务。 - davidsbroParallel.ForEachAsync
或类似方法来限制并发SendPingAsync
操作的数量。不过,我并不指望这个方法能够解决问题。 - Theodor ZouliasTask.WhenAny
已经是修复它的一种尝试了吗?考虑到ping有自己的超时,PingMachine方法中的许多内容似乎是多余的。 - GolezTrolPing.SendAsync
使用同步的Dns.GetAddresses
,即使它应该使用异步。因此,将这两个操作分开并手动调用Dns.GetHostAddressesAsync
,然后仅将 IP 地址传递给Ping
,这样做可能是值得的。您可以在 Framework 4 的参考源代码 中看到此错误,并在 Runtime 5.0+ 中看到修复方法。 - Charlieface