c# - 异步ping

标签 c# events asynchronous ping

遇到了一个奇怪的“问题”。拥有一个可以扫描整个网络的应用程序。在您进入具有 255.255.0.0 网络掩码(即 65k + 地址)的网络之前效果很好。

我这样发送 ping:

    foreach (string str in ListContainingAddresses)
        {
            using (Ping ping = new Ping())
            {
                if (pingCounter == 10000) { Thread.Sleep(10000); pingCounter = 0; }
                //Make an eventhandler
                ping.PingCompleted += new PingCompletedEventHandler(pingCompleted);
                //Send the pings asynchronously
                ping.SendAsync(IPAddress.Parse(str), 1000);
                sentPings++;

                //This counts pings being sent out
                pingCounter++;
            }
        }

然后像这样接收它们:

    public void pingCompleted(object sender, PingCompletedEventArgs e)
    {
        //This counts recieved addresses 
        recievedIpAddresses++;

        if (e.Reply.Status == IPStatus.Success)
        {
            //Do something
        }
        else
        {
            /*Computer is down*/
        }
        //This checks if sent equals recieved
        if (recievedIpAddresses == sentPings )
        {
            //All returned
        }
    }

问题是 a) 有时(很少)它没有完成(不满足条件)。 b) 当它完成时数字不匹配?如果我现在打印已发送和已收到,它们是

    Sent: 65025 Recieved: 64990

尽管如此,条件是否满足并且应用程序继续?我不知道为什么以及如何发生这种情况。应用程序更新两个整数的代码执行速度是否过快?一些 ping 会在此过程中丢失吗?如果我在一个有 255 个地址的子网上尝试,这个问题永远不会发生。 不能使用 CountDownEvent 而不是变量,因为它是 .NET 3.5

最佳答案

你有任何锁定吗?对我来说,这看起来像你的问题。我可以看到各种 race conditions and memory processor cache issues可能在您的代码中。

尝试使用 lock用于保护 recievedIpAddresses == sentPings

sentPings++;
//This counts pings being sent out
pingCounter++;

使用

例如:

private readonly object SyncRoot = new object();

public void MainMethod()
{
    foreach (string str in ListContainingAddresses)
    { ... }
    lock (SyncRoot) { sentPings++; }
    ....
}

public void pingCompleted(object sender, PingCompletedEventArgs e)
{
    //This counts recieved addresses 
    lock (SyncRoot) { recievedIpAddresses++; } // lock this if it is used on other threads

    if (e.Reply.Status == IPStatus.Success)
    {
        //Do something
    }
    else
    {
        /*Computer is down*/
    }
    lock (SyncRoot) { // lock this to ensure reading the right value of sentPings
        //This checks if sent equals recieved
        if (recievedIpAddresses == sentPings )
        {
            //All returned
        }
    }
}

上述示例将强制从共享内存读取和写入,以便不同的 CPU 核心不会读取不同的值。但是,根据您的代码,您可能需要更粗粒度的锁定,其中第一个循环在一个 lock 中同时保护 sentPingspingCounter,并且甚至第二种方法也可能完全受到 lock 的保护。

人们可以说不要使用 lock 因为它会导致性能问题,而 lock free 非常流行。底线是 lock 大多数时候比其他替代方案更简单。您可能需要使您的锁定比上面的示例更粗粒度,因为您也可能有竞争条件。如果没有看到您的整个程序,很难给出更好的示例。

互锁.Increment

这里使用 lock 的主要原因是强制每次读写都来自内存,而不是 CPU 缓存,因此您应该获得一致的值。锁定的替代方法是使用 Interlocked.Increment ,但如果您将它用于两个单独的变量,则需要仔细观察竞争条件。

竞争条件

(编辑)

即使您锁定了也可能会遇到问题。观看此时间线以了解 13 个目标地址(对某些人来说不走运)。如果您不明白这是为什么,请查看 "Managed Threading Basics""Threading in C# - Joseph Albahari"

  • T1:1 次
    • T1:Ping 已发送
    • T1:sentPings++
  • T2:1 次
    • 收到IpAddresses++;
    • T2:其他内容
  • 同时 T1:12 次
    • T1:Ping 已发送
    • T1:sentPings++(现在等于 13)
  • T2:recievedIpAddresses == sentPings 测试 - 现在失败,因为它们不相等
  • T3 到 T14:输入 pingCompleted 并执行 recievedIpAddresses++;
  • T1 完成,应用程序开始写出 ping 计数(或者更糟的是仍然完全退出),然后其他 12 个线程在后台返回

您需要仔细观察代码中的此类竞争条件,并相应地进行调整。关于线程的全部事情是它们重叠它们的操作。

同步根

脚注:

为什么 SyncRoot 声明为:private readonly object SyncRoot = new object();

  • 它是一个保护类字段的类字段,如果你有一个static控制台应用程序,它需要是static。但是,如果你在一个类中使用 static,那么每个实例都会锁定同一个对象,所以会有争用
  • 声明意图是只读,防止您(或其他团队成员)稍后覆盖它
  • 它是一个对象:
    • 除了一个对象你不需要任何东西
    • 你不能锁定一个值类型
    • 你不应该锁定你的类实例(以防止在更复杂的代码中出现deadlocks)
    • 你不应该公开它(也是为了防止死锁)
  • 它通过这个语句与类一起实例化(以线程安全的方式)
  • SyncRoot为例; Visual Studio 历来在它的片段中这样调用它

关于c# - 异步ping,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16853746/

相关文章:

Python 正确查找和读取 Windows 应用程序事件日志

c# - ffmpeg c# asp.net视频转换错误

.net - 在 .NET 框架中引发事件的正确方法

c# - 如何以编程方式格式化 Excel 工作表中的单元格?

javascript - AlloyUI Scheduler - 从数据库加载后添加自定义属性

javascript - 您如何处理在异步函数之间共享的变量的拼接?

javascript - 异步 JavaScript 轮询使用 setTimeout 或 setInterval 卡住浏览器

node.js - 如何让 Nock 和 Mocha 一起玩?

c# - 反序列化动态 JSON 文件 C# NewtonSoft.JSON

c# - 实例化一个 GameObject 会导致所述对象的 Transform 被破坏?