如何同时异步地进行多个ping操作?

10

我似乎无法理解如何结构化异步调用SendPingAsync。目前我想循环遍历一个IP地址列表并在程序继续之前对它们进行异步ping...现在一次只能一个地去ping所有的地址,这需要很长时间。我之前问过一个关于这个问题的问题,以为自己可以弄懂异步但显然我错了。

private void button1_Click(object sender, EventArgs e)
{
    this.PingLoop();
    MessageBox.Show("hi"); //for testing

}

public async void PingLoop()
{
    Task<int> longRunningTask = PingAsync();

    int result = await longRunningTask;
    MessageBox.Show("async call is finished!"); 

    //eventually want to loop here but for now just want to understand how this works
}

private async Task<int> PingAsync()
{
    Ping pingSender = new Ping();
    string reply = pingSender.SendPingAsync("www.google.com", 2000).ToString();
    pingReplies.Add(reply); //what should i be awaiting here?? 
    return 1;
}

我恐怕对这里到底发生了什么不是很清楚...在什么情况下应该返回任务?当我按照原样运行时,界面会冻结并出现ping错误。我阅读了MSDN文档和大量的问题,但仍然无法理解。

5个回答

17

你需要做的是:

private async Task<List<PingReply>> PingAsync()
{
    var tasks = theListOfIPs.Select(ip => new Ping().SendPingAsync(ip, 2000));
    var results = await Task.WhenAll(tasks);

    return results.ToList();
}

这将异步地从theListOfIPs中的每个IP启动一个请求,然后异步等待它们全部完成。然后它将返回回复列表。

请注意,通常最好返回结果而不是将其设置为字段。 后者可能会导致错误,如果在异步操作完成之前尝试使用该字段(pingReplies) - 通过在调用await后返回并将范围添加到您的集合,可以使代码更清晰、更少出错。


8
我必须修改“ip => pingSender.SendPingAsync(ip, 2000)”为“ip => new Ping().SendPingAsync(ip, 2000)”,否则会出现“System.InvalidOperationException:已经有一个异步调用正在进行中。必须在调用此方法之前完成或取消它。”的错误提示。 - pingo
什么是ListOfIPs? - software is fun
@softwareisfun 一个IP地址的List<string>。 - Pierre Nortje

2
你想要的是同时启动所有的ping:
var pingTargetHosts = ...; //fill this in
var pingTasks = pingTargetHosts.Select(
     host => new Ping().SendPingAsync(host, 2000)).ToList();

现在ping正在运行。收集它们的结果:
var pingResults = await Task.WhenAll(pingTasks);

现在处理的并发阶段完成了,您可以检查和处理结果。

为什么在使用WhenAll时还要对pingTasks使用ToList()呢? - Reed Copsey
@ReedCopsey 习惯,以获得更可靠的副作用。我可能稍后再次枚举 pingTasks - usr

2

你在这里做的事情 pingSender.SendPingAsync("www.google.com", 2000).ToString(); 没有太多意义。

相反,你应该返回 pingSender.SendPingAsync("www.google.com", 2000)

await Task.WhenAll(你所有的ping请求)


0
这是我做的方法。
private delegate void scanTargetDelegate(IPAddress ipaddress);

private Task<PingReply> pingAsync(IPAddress ipaddress)
    {
        var tcs = new TaskCompletionSource<PingReply>();
        try
        {

            AutoResetEvent are = new AutoResetEvent(false);

            Ping ping = new Ping();
            ping.PingCompleted += (obj, sender) =>
                {
                    tcs.SetResult(sender.Reply);
                };
            ping.SendAsync(ipaddress, new object { });

        }
        catch (Exception)
        {
        }
        return tcs.Task;
    }

在 BackgroundWorker 中,我这样做:
List<Task<PingReply>> pingTasks = new List<Task<PingReply>>();

        addStatus("Scanning Network");
        foreach (var ip in range)
        {
            pingTasks.Add(pingAsync(ip));
        }

        Task.WaitAll(pingTasks.ToArray());

        addStatus("Network Scan Complete");

        scanTargetDelegate d = null;
        IAsyncResult r = null;

foreach (var pingTask in pingTasks)
        {
            if (pingTask.Result.Status.Equals(IPStatus.Success))
            {
                d = new scanTargetDelegate(scanTarget); //do something with the ip
                r = d.BeginInvoke(pingTask.Result.Address, null, null);
                Interlocked.Increment(ref Global.queuedThreads);
            }
            else
            {
                if (!ownIPs.Contains(pingTask.Result.Address))
                {
                    failed.Add(pingTask.Result.Address);
                }
            }
        }

        if (r != null)
        {
            WaitHandle[] waits = new WaitHandle[] { r.AsyncWaitHandle };
            WaitHandle.WaitAll(waits);
        }

2
你不需要这样做 - Ping 已经支持异步操作: http://msdn.microsoft.com/zh-cn/library/hh193994(v=vs.110).aspx - Reed Copsey
那样做会更容易。 - Tsukasa

0
public static async Task<bool> PingAsync(string host)
{
    try
    {
        var ping = new System.Net.NetworkInformation.Ping();
        var reply = await ping.SendTaskAsync(host);

        return (reply.Status == System.Net.NetworkInformation.IPStatus.Success);
    }
    catch { return false; }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接