简体   繁体   English

Ping 任务将无法完成

[英]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.DelayCancellationToken在 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

VS 任务窗口中等待的任务列表的图像

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM