[英]Ping Tasks will not complete
I am working on a "heartbeat" application that pings hundreds of IP addresses every minute via a loop.我正在开发一个“心跳”应用程序,它每分钟通过一个循环 ping 数百个 IP 地址。 The IP addresses are stored in a list of a class
Machines
. IP 地址存储在 class
Machines
列表中。 I have a loop that creates a Task<MachinePingResults>
(where MachinePingResults
is basically a Tuple of an IP and online status) for each IP and calls a ping function using System.Net.NetworkInformation
.我有一个循环,它为每个 IP 创建一个
Task<MachinePingResults>
(其中MachinePingResults
基本上是一个 IP 和在线状态的元组)并使用System.Net.NetworkInformation
调用 ping ZC1C425268E68385D1AB5074
The issue I'm having is that after hours (or days) of running, one of the loops of the main program fails to finish the Tasks
which is leading to a memory leak.我遇到的问题是,运行数小时(或数天)后,主程序的一个循环无法完成导致 memory 泄漏的
Tasks
。 I cannot determine why my Tasks are not finishing (if I look in the Task list during runtime after a few days of running, there are hundreds of tasks that appear as "awaiting").我无法确定为什么我的任务没有完成(如果我在运行几天后在运行时查看任务列表,有数百个任务显示为“等待”)。 Most of the time all the tasks finish and are disposed;
大多数时候,所有任务都完成并被处理; it is just randomly that they don't finish.
他们没有完成只是随机的。 For example, the past 24 hours had one issue at about 12 hours in with 148 awaiting tasks that never finished.
例如,过去 24 小时有一个问题,大约在 12 小时内有 148 个等待任务从未完成。 Due to the nature of not being able to see why the
Ping
is hanging (since it's internal to .NET), I haven't been able to replicate the issue to debug.由于无法查看
Ping
为何挂起的性质(因为它在 .NET 内部),我无法复制该问题以进行调试。
(It appears that the Ping
call in .NET can hang and the built-in timeout fail if there is a DNS issue, which is why I built an additional timeout in) ( 似乎.NET 中的
Ping
调用可能会挂起,并且如果存在 DNS 问题,则内置超时会失败,这就是我在其中构建额外超时的原因)
I have a way to cancel the main loop if the pings don't return within 15 seconds using Task.Delay
and a CancellationToken
.如果使用
Task.Delay
和CancellationToken
在 15 秒内没有返回 ping,我有办法取消主循环。 Then in each Ping function I have a Delay
in case the Ping call itself hangs that forces the function to complete.然后在每个 Ping function 中我有一个
Delay
,以防 Ping 调用本身挂起,这会强制 function 完成。 Also note I am only pinging IPv4;另请注意,我只 ping IPv4; there is no IPv6 or URL.
没有 IPv6 或 URL。
Main Loop主循环
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();
}
The Ping Function平Function
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);
}
With every Task
having a Delay to let it continue if the Ping hangs, I don't know what would be the issue that is causing some of the Task<MachinePingResults>
to never finish.如果 Ping 挂起,每个
Task
都有延迟让它继续,我不知道是什么问题导致某些Task<MachinePingResults>
永远无法完成。
How can I ensure a Task
using .NET Ping
ends?如何确保使用 .NET
Ping
结束的Task
?
There are quite a few gaps in the code posted, but I attempted to replicate and in doing so ended up refactoring a bit.发布的代码中有很多空白,但我尝试复制并最终进行了一些重构。
This version seems pretty robust, with the actual call to SendAsync
wrapped in an adapter class.这个版本看起来相当健壮,对
SendAsync
的实际调用包含在适配器 class 中。
I accept this doesn't necessarily answer the question directly, but in the absence of being able to replicate your problem exactly, offers an alternative way of structuring the code that may eliminate the problem.我接受这不一定直接回答问题,但在无法准确复制您的问题的情况下,提供了另一种结构化代码的方法,可以消除问题。
async Task Main()
{
var masterCts = new CancellationTokenSource(TimeSpan.FromSeconds(15)); // 15s overall timeout
var localMachines = new List<LocalMachine>
{
new LocalMachine("192.0.0.1", false), // Should be not known - TimedOut
new LocalMachine("192.168.86.88", false), // Should be not known - DestinationHostUnreachable (when timeout is 8000)
new LocalMachine("www.dfdfsdfdfdsgrdf.cdcc", false), // Should be not known - status Unknown because of PingException
new LocalMachine("192.168.86.87", false) // Known - my local IP
};
var results = new List<PingerResult>();
try
{
// Create the "hot" tasks
var tasks = localMachines.Where(m => !m.Online)
.Select(m => new Pinger().SendPingAsync(m.HostOrAddress, 8000, masterCts.Token))
.ToArray();
await Task.WhenAll(tasks);
results.AddRange(tasks.Select(t => t.Result));
}
finally
{
results.ForEach(r => localMachines.Single(m => m.HostOrAddress.Equals(r.HostOrAddress)).Online = r.Status == IPStatus.Success);
results.Dump(); // For LINQPad
localMachines.Dump(); // For LINQPad
results.Clear();
}
}
public class LocalMachine
{
public LocalMachine(string hostOrAddress, bool online)
{
HostOrAddress = hostOrAddress;
Online = online;
}
public string HostOrAddress { get; }
public bool Online { get; set; }
}
public class PingerResult
{
public string HostOrAddress {get;set;}
public IPStatus Status {get;set;}
}
public class Pinger
{
public async Task<PingerResult> SendPingAsync(string hostOrAddress, int timeout, CancellationToken token)
{
// Check if timeout has occurred
token.ThrowIfCancellationRequested();
IPStatus status = default;
try
{
var reply = await SendPingInternal(hostOrAddress, timeout, token);
status = reply.Status;
}
catch (PingException)
{
status = IPStatus.Unknown;
}
return new PingerResult
{
HostOrAddress = hostOrAddress,
Status = status
};
}
// Wrap the legacy EAP pattern offered by Ping.
private Task<PingReply> SendPingInternal(string hostOrAddress, int timeout, CancellationToken cancelToken)
{
var tcs = new TaskCompletionSource<PingReply>();
if (cancelToken.IsCancellationRequested)
{
tcs.TrySetCanceled();
}
else
{
using (var ping = new Ping())
{
ping.PingCompleted += (object sender, PingCompletedEventArgs e) =>
{
if (!cancelToken.IsCancellationRequested)
{
if (e.Cancelled)
{
tcs.TrySetCanceled();
}
else if (e.Error != null)
{
tcs.TrySetException(e.Error);
}
else
{
tcs.TrySetResult(e.Reply);
}
}
};
cancelToken.Register(() => { tcs.TrySetCanceled(); });
ping.SendAsync(hostOrAddress, timeout, new object());
}
};
return tcs.Task;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.