【问题标题】:C# multiple pinging in loopC# 循环中的多个 ping
【发布时间】:2014-08-22 14:33:15
【问题描述】:

我需要创建将循环ping 多个地址的应用程序。我在 stackoverflow 上阅读了很多示例,最后得到了工作代码:

    public void Check(List<string> addresses)
    {
        List<Task<PingReply>> pingTasks = new List<Task<PingReply>>();
        foreach (string address in addresses)
        {
            pingTasks.Add(PingAsync(address));
        }

        Task.Factory.ContinueWhenAll(pingTasks.ToArray(), _ => { }).ContinueWith(t =>
        {
            StringBuilder pingResult = new StringBuilder();
            foreach (var pingTask in pingTasks)
            {
                pingResult.Append(pingTask.Result.Address);
                pingResult.Append("    ");
                pingResult.Append(pingTask.Result.Status);
                pingResult.Append("    ");
                pingResult.Append(pingTask.Result.RoundtripTime.ToString());
                pingResult.Append("   \n");
            }
            Console.WriteLine(pingResult.ToString());
        },
        CancellationToken.None,
        TaskContinuationOptions.None,
        TaskScheduler.FromCurrentSynchronizationContext());
    }

    public static Task<PingReply> PingAsync(string address)
    {
        var tcs = new TaskCompletionSource<PingReply>();
        using (Ping ping = new Ping())
        {
            ping.PingCompleted += (obj, sender) =>
            {
                tcs.SetResult(sender.Reply);
            };
            ping.SendAsync(address, new object());
        }
        return tcs.Task;
    }

现在我需要更改此代码以与 await 和 async 一起使用,然后以间隔循环执行此代码。我的问题从这里开始。我不知道在这种情况下如何使用异步,我阅读了很多文章,现在我很困惑,因为我的代码仍然无法工作。您能逐步解释一下如何更改我的代码以使用等待吗?你能解释一下我怎样才能把它放在带有执行间隔的while循环中吗?我试图将整个“检查”函数放入循环中并在最后添加 Thread.Sleep(interval) 但我有一种奇怪的感觉,我做错了/效率低下。我需要在 1 秒内 ping 400 台服务器。甚至可能吗? 问候

更新 1: 到目前为止我有代码:

using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net.NetworkInformation;
using System.Linq;

namespace Pinging
{
    class CheckPing
    {
        public async Task LoopAndCheckPingAsync(List<string> addresses)
        {
            while (true)
            {
                var ping = new Ping();
                var pingTasks = addresses.Select(address => ping.SendPingAsync(address));

                await Task.WhenAll(pingTasks);

                StringBuilder pingResultBuilder = new StringBuilder();

                foreach (var pingReply in pingTasks)
                {
                    pingResultBuilder.Append(pingReply.Result.Address);
                    pingResultBuilder.Append("    ");
                    pingResultBuilder.Append(pingReply.Result.Status);
                    pingResultBuilder.Append("    ");
                    pingResultBuilder.Append(pingReply.Result.RoundtripTime.ToString());
                    pingResultBuilder.AppendLine();
                }

                Console.WriteLine(pingResultBuilder.ToString());

                await Task.Delay(TimeSpan.FromMinutes(5));
            }
        }
    }
}

并调用:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Pinging
{
    public class Class1
    {
        static void Main()
        {
            List<string> addresses = new List<string>() { "www.google.pl", "212.77.100.101" };

            CheckPing c = new CheckPing();
            Task.Factory.StartNew(() => c.LoopAndCheckPingAsync(addresses));

            Console.Read();
        }
    }
}

我尝试以不同的方式从 Main 调用 LoopAndCheckPingAsync,但仍然冻结。这是我最后一次尝试。

编辑 2: 我做了一些更改以查看应用程序性能,现在我的代码如下所示:

using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net.NetworkInformation;
using System.Linq;

namespace Pinging
{
    class CheckPing
    {
        public async Task LoopAndCheckPingAsync(List<string> addresses)
        {
            while (true)
            {
                var pingTasks = addresses.Select(address =>
                {
                    return new Ping().SendPingAsync(address);
                });

                await Task.WhenAll(pingTasks);

                StringBuilder pingResultBuilder = new StringBuilder();

                foreach (var pingReply in pingTasks)
                {
                    pingResultBuilder.Append(pingReply.Result.Address);
                    pingResultBuilder.Append("    ");

                    pingResultBuilder.Append(pingReply.Result.Status);
                    pingResultBuilder.Append("    ");

                    pingResultBuilder.Append(pingReply.Result.RoundtripTime.ToString());
                    pingResultBuilder.AppendLine();
                }

                Console.WriteLine(pingResultBuilder.ToString());
                Functions.counter++;

                if (Functions.counter >= 100) break;

                await Task.Delay(TimeSpan.FromSeconds(1));
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Pinging
{
    public class Class1
    {
        static void Main()
        {
            List<string> addresses = Functions.Read(@"C:\Users\Adam\Desktop\addresses.csv");

            Functions.start = DateTime.Now;

            CheckPing c = new CheckPing();
            c.LoopAndCheckPingAsync(addresses).Wait();

            Console.WriteLine(Functions.counter);

            Console.Read();
        }
    }
}

我正在使用从文件中读取的标准站点地址:

www.google.com
www.yahoo.com
www.live.com
www.msn.com
www.facebook.com
www.youtube.com
www.microsoft.com
www.wikipedia.org
www.myspace.com
www.ebay.com
www.aol.com
www.ask.com
www.craigslist.org
www.blogspot.com
www.answers.com
www.about.com
www.amazon.com
www.mapquest.com
www.windows.com
www.adobe.com
www.photobucket.com
www.wordpress.com
www.go.com
www.paypal.com
www.walmart.com
www.reference.com
www.cnn.com
www.twitter.com
www.imdb.com
www.flickr.com
www.att.com
www.cnet.com
www.irs.gov
www.whitepages.com
www.yellowpages.com
www.comcast.net
www.target.com
www.simplyhired.com
www.webmd.com
www.weather.com
www.blogger.com
www.bankofamerica.com
www.apple.com
www.chase.com
www.bizrate.com
www.hulu.com
www.merriam-webster.com
www.geocities.com
www.ehow.com
www.ezinearticles.com

编辑 3: 现在一切正常,但这是我需要处理的另一个问题。当我在 5 分钟后测试 100000 次 ping 时,出现内存不足异常。有没有办法以某种方式处理这个问题?也许划分成块并销毁旧类?

编辑 4: 错误内容:

System.OutOfMemoryException 未处理 HResult=-2147024882 引发了“System.OutOfMemoryException”类型的消息 = 异常。 来源=mscorlib 堆栈跟踪: 在 System.Exception.Init() 在 System.InvalidOperationException..ctor(字符串消息,异常 innerException) 在 System.Net.NetworkInformation.PingException..ctor(字符串消息,异常 innerException) 在 System.Net.NetworkInformation.Ping.ContinueAsyncSend(对象状态) 在 System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(对象 状态) 在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext、ContextCallback 回调、对象状态、布尔值 保留SyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback 回调, 对象状态, Boolean 保留SyncCtx) 在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 在 System.Threading.ThreadPoolWorkQueue.Dispatch() 在 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
内部异常:

EDIT 5 添加 using 语句后,我得到“没有足够的存储空间来处理此命令”错误:

System.AggregateException 未处理 HResult=-2146233088
Message=发生了一个或多个错误。来源=mscorlib 堆栈跟踪: 在 System.Threading.Tasks.Task.ThrowIfExceptional(布尔 includeTaskCanceledExceptions) 在 System.Threading.Tasks.Task.Wait(Int32 毫秒超时,CancellationToken 取消令牌) 在 System.Threading.Tasks.Task.Wait() 在 Pinging.Class1.Main() 在 System.AppDomain._nExecuteAssembly(RuntimeAssembly 程序集,字符串 [] 参数) 在 System.AppDomain.ExecuteAssembly(字符串 assemblyFile,证据 assemblySecurity,String [] args) 在 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 在 System.Threading.ThreadHelper.ThreadStart_Context(对象状态) 在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext、ContextCallback 回调、对象状态、布尔值 保留SyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback 回调, 对象状态, Boolean 保留SyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback 回调,对象状态) 在 System.Threading.ThreadHelper.ThreadStart() InnerException: System.Net.NetworkInformation.PingException H结果=-2146233079 Message=Ping 请求期间发生异常。 源=mscorlib 堆栈跟踪: 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务 任务) 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务 任务) 在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() 在 Pinging.CheckPing.d__2.MoveNext() 内部异常:System.ApplicationException H结果=-2147024888 消息=没有足够的存储空间来处理此命令(来自 HRESULT 的异常:0x80070008) 源=mscorlib 堆栈跟踪: 在 System.Threading.ThreadPool.RegisterWaitForSingleObjectNative(WaitHandle waitHandle,对象状态,UInt32 timeOutInterval,布尔值 executeOnlyOnce, RegisteredWaitHandle 注册WaitHandle, StackCrawlMark& stackMark,布尔压缩堆栈) 在 System.Threading.ThreadPool.RegisterWaitForSingleObject(WaitHandle waitObject,WaitOrTimerCallback 回调,对象状态,UInt32 毫秒TimeOutInterval,布尔型executeOnlyOnce,StackCrawlMark& stackMark,布尔压缩堆栈) 在 System.Threading.ThreadPool.RegisterWaitForSingleObject(WaitHandle waitObject,WaitOrTimerCallback 回调,对象状态,Int32 毫秒TimeOutInterval,布尔型executeOnlyOnce) 在 System.Net.NetworkInformation.Ping.InternalSend(IPAddress 地址, Byte[] 缓冲区,Int32 超时,PingOptions 选项,布尔异步) 在 System.Net.NetworkInformation.Ping.ContinueAsyncSend(对象状态) 内部异常:

【问题讨论】:

    标签: c# .net multithreading async-await ping


    【解决方案1】:

    让我们来看看我们想要做什么:

    1. 我们想在第一次执行方法时开始while循环

    2. 我们想要生成一组任务,用于发送 ping 请求。为此,我们可以使用Ping.SendPingAsync。我们将使用Enumerable.Select

    3. 投影地址列表中的每个元素
    4. 我们将等到所有任务完成执行。为此,我们将在Task.WhenAllawait

    5. 当所有任务执行完 ping 请求后,我们将使用 foreach 循环对其进行迭代。

    6. 我们将等待调用之间的间隔时间。我们不会使用Thread.Sleep,因为它是一个阻塞调用。我们将改为使用Task.Delay 将在内部使用Timer。当我们等待它时,控制权将返回给调用我们的方法。

    结果是这样的:

    private static async Task LoopAndCheckPingAsync(List<string> addresses)
    {  
        StringBuilder pingResultBuilder = new StringBuilder();        
    
        while (true)
        {
             var pingTasks = addresses.Select(address =>
             {
                 using (var ping = new Ping())
                 {
                     return ping.SendPingAsync(address);
                 }
             }).ToList();    
    
            await Task.WhenAll(pingTasks);
    
            foreach (var pingReply in pingTasks)
            {                pingResultBuilder.Append(pingReply.Result.Address);
                pingResultBuilder.Append("    ");
                pingResultBuilder.Append(pingReply.Result.Status);
                pingResultBuilder.Append("    ");
    
                pingResultBuilder.Append(pingReply.Result.RoundtripTime.ToString());
                pingResultBuilder.AppendLine();
            }
    
            Console.WriteLine(pingResultBuilder.ToString());
            pingResultBuilder.Clear();
    
            await Task.Delay(TimeSpan.FromMinutes(5));
        }
    }
    

    请注意,该方法现在返回 Task 而不是 void,因为我们需要在我们的方法上使用 await(注意,一旦您开始使用它,async 往往会在您的代码库中传播)。

    编辑

    在深入了解Ping class 之后,显然我们无法在同一个Ping 实例上执行多个ping 请求(查看Ping.CheckStart,它会检查是否有正在进行的请求,如果有则抛出一个InvalidOperationException),这正是我们在 Select 方法中所做的。为了解决这个问题,我们可以为每个请求创建一个Ping 类的实例。请注意,这会给您的应用程序增加一些内存压力。如果您有 1000 个请求同时进行,这意味着在发出这些请求时,您将在内存中拥有 1000 个 Ping 类的实例。

    要注意的另一件事是您在控制台应用程序中运行,该应用程序在内部使用 ThreadPoolSynchronizationContext。无需调用Task.Run 来执行我们的方法,您可以使用Task.Wait 在发出请求时保持控制台应用程序处于活动状态。最好使用Wait,这样我们可以查看我们的方法是否传播了任何异常。

    static void Main()
    {
        List<string> addresses = new List<string>() { "www.google.pl", "212.77.100.101" };
    
        CheckPing c = new CheckPing();
        c.LoopAndCheckPingAsync(addresses).Wait();
    }
    

    【讨论】:

    • 您好,我的应用程序还有另一个问题,希望您能帮助我。一切正常,但如果任何 ping 无法访问,我会收到以下回复:0.0.0.0 超时。有没有办法(快速)检索发送的主机名而不是地址?我在这里找到了一些解决方案,但它们都使用 ping.SendAsync 而不是 ping.SendPingAsync。
    • @AdamMrozek 请把它作为一个新问题发布,我会看看它:)
    • 这是我的问题。谢谢 :) stackoverflow.com/questions/25534085/…
    【解决方案2】:

    我建议您为此考虑使用 Microsoft 的响应式框架 (NuGet "Rx-Main")。这就是代码的样子:

    private static void LoopAndCheckPingAsync(List<string> addresses)
    {
        Func<string, IObservable<PingReply>> getPingReply = a =>
            Observable.Using(
                () => new Ping(),
                p => Observable.FromAsync<PingReply>(() => p.SendPingAsync(a)));
    
        var query =
            from n in Observable.Interval(TimeSpan.FromSeconds(5)).StartWith(-1L)
            from ps in
            (
                from a in addresses.ToObservable()
                from pr in getPingReply(a)
                select pr
            ).ToArray()
            select String.Join(
                Environment.NewLine,
                ps.Select(p => String.Format("{0}    {1}    {2}",
                        p.Address,
                        p.Status,
                        p.RoundtripTime)));
    
        query.Subscribe(x => Console.WriteLine(x));
    }
    

    据我所知,这都是异步的,并且符合您的要求。

    【讨论】:

      猜你喜欢
      • 2018-08-22
      • 2022-12-29
      • 2011-09-01
      • 1970-01-01
      • 2013-05-27
      • 2016-08-03
      • 1970-01-01
      • 1970-01-01
      • 2017-12-22
      相关资源
      最近更新 更多