C#在循环中多次ping

时间:2014-08-22 14:33:15

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

我需要创建将在循环中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,然后在循环中使用interval执行此操作。在这里我的问题开始了。在这种情况下,我不知道如何使用异步,我阅读了很多文章,现在我很困惑,因为我的代码仍然无法正常工作。你能一步一步地向我解释如何改变我的代码以便等待吗?你能解释一下我怎么能在执行间隔的循环中把它放进去?我试图把整个&#39;检查&#39;函数进入循环并在最后添加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   消息=类型的异常&#39; System.OutOfMemoryException&#39;被扔了。   Source = mscorlib StackTrace:          在System.Exception.Init()          at System.InvalidOperationException..ctor(String message,Exception innerException)          在System.Net.NetworkInformation.PingException..ctor(String message,Exception innerException)          在System.Net.NetworkInformation.Ping.ContinueAsyncSend(对象状态)          在System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object   州)          在System.Threading.ExecutionContext.RunInternal(ExecutionContext   executionContext,ContextCallback回调,对象状态,布尔值   preserveSyncCtx)          at System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback callback,Object state,Boolean   preserveSyncCtx)          在System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()          在System.Threading.ThreadPoolWorkQueue.Dispatch()          在System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
  的InnerException:

编辑5 添加使用语句后,我得到&#34;没有足够的存储空间来处理此命令&#34;错误:

  

System.AggregateException未处理HResult = -2146233088
  消息=发生了一个或多个错误。 Source = mscorlib StackTrace:          在System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)          在System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout,CancellationToken cancellationToken)          在System.Threading.Tasks.Task.Wait()          在Pinging.Class1.Main()          在System.AppDomain._nExecuteAssembly(RuntimeAssembly程序集,String [] args)          在System.AppDomain.ExecuteAssembly(String assemblyFile,Evidence assemblySecurity,String [] args)          在Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()          在System.Threading.ThreadHelper.ThreadStart_Context(对象状态)          在System.Threading.ExecutionContext.RunInternal(ExecutionContext   executionContext,ContextCallback回调,对象状态,布尔值   preserveSyncCtx)          at System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback callback,Object state,Boolean   preserveSyncCtx)          在System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback回调,对象状态)          at System.Threading.ThreadHelper.ThreadStart()InnerException:System.Net.NetworkInformation.PingException          的HResult = -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()          InnerException:System.ApplicationException               的HResult = -2147024888               消息=没有足够的存储空间可用于处理此命令(来自HRESULT的异常:0x80070008)               来源= mscorlib程序               堆栈跟踪:                    在System.Threading.ThreadPool.RegisterWaitForSingleObjectNative(WaitHandle   waitHandle,对象状态,UInt32 timeOutInterval,布尔值   executeOnlyOnce,RegisteredWaitHandle registeredWaitHandle,   StackCrawlMark&安培; stackMark,Boolean compressStack)                    在System.Threading.ThreadPool.RegisterWaitForSingleObject(WaitHandle   waitObject,WaitOrTimerCallback callBack,Object state,UInt32   millisecondsTimeOutInterval,Boolean executeOnlyOnce,StackCrawlMark&amp;   stackMark,Boolean compressStack)                    在System.Threading.ThreadPool.RegisterWaitForSingleObject(WaitHandle   waitObject,WaitOrTimerCallback callBack,Object state,Int32   millisecondsTimeOutInterval,Boolean executeOnlyOnce)                    在System.Net.NetworkInformation.Ping.InternalSend(IPAddress地址,   字节[]缓冲区,Int32超时,PingOptions选项,布尔异步)                    在System.Net.NetworkInformation.Ping.ContinueAsyncSend(对象状态)               的InnerException:

2 个答案:

答案 0 :(得分:5)

让我们来看看我们想做的事情:

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

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

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

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

  6. 我们将等待通话之间的间隔时间。我们不会使用Thread.Sleep,因为它是一个阻止调用。我们将使用Task.Delay internaly使用Timer。当我们等待它时,控制将回到调用我们的方法。

  7. 这就是它的结果:

    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,检查是否有&#39} ; s是一个持续的请求,如果有一个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();
    }
    

答案 1 :(得分:0)

我建议您考虑使用Microsoft的Reactive Framework(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));
}

就我所见,它都是异步并且遵循你的要求。