【问题标题】:Pinging all computers in network with TPL使用 TPL ping 网络中的所有计算机
【发布时间】:2014-07-01 12:06:49
【问题描述】:

我正在开发一个管理网络中设备的应用程序,在应用程序中的某个点,我必须 ping(实际上它不是 ping,它是 SNMP get)网络中的所有计算机以检查其类型是否为我的受管设备。

我的问题是 ping 网络中的所有计算机非常慢(特别是因为它们中的大多数都不会响应我的消息并且只会超时)并且必须异步完成。

我尝试使用 TLP 通过以下代码执行此操作:

public static void FindDevices(Action<IPAddress> callback)
{
    //Returns a list of all host names with a net view command
    List<string> hosts = FindHosts();

    foreach (string host in hosts)
    {
        Task.Run(() =>
        {
            CheckDevice(host, callback);
        });
    }
}

但是它运行非常慢,当我暂停执行时,我检查了线程窗口,发现它只有一个线程 ping 网络,因此同步运行任务。

当我使用普通线程时,它运行得更快,但任务应该更好,我想知道为什么我的任务没有优化并行性。

**编辑** 评论要求 CheckDevice 上的代码,所以这里是:

    private static void CheckDevice(string host, Action<IPAddress> callback)
    {
        int commlength, miblength, datatype, datalength, datastart;
        string output;
        SNMP conn = new SNMP();


        IPHostEntry ihe;
        try
        {
            ihe = Dns.Resolve(host);
        }
        catch (Exception)
        {
            return;
        }
        // Send sysLocation SNMP request
        byte[] response = conn.get("get", ihe.AddressList[0], "MyDevice", "1.3.6.1.2.1.1.6.0");

        if (response[0] != 0xff)
        {
            // If response, get the community name and MIB lengths
            commlength = Convert.ToInt16(response[6]);
            miblength = Convert.ToInt16(response[23 + commlength]);

            // Extract the MIB data from the SNMP response
            datatype = Convert.ToInt16(response[24 + commlength + miblength]);
            datalength = Convert.ToInt16(response[25 + commlength + miblength]);
            datastart = 26 + commlength + miblength;
            output = Encoding.ASCII.GetString(response, datastart, datalength);
            if (output.StartsWith("MyDevice"))
            {
                callback(ihe.AddressList[0]);
            }
        }
    }

【问题讨论】:

  • 这可能取决于 CheckDevice 的实际作用
  • 另外,List 不是线程安全的,不应使用。 You should use a ConcurrentBag。这可能是您问题的根源。 CheckDevice 是否有任何机会锁定它?
  • 到目前为止我还没有发现问题。让我们尝试一下。将 dns 检查替换为 Task.Run(() =&gt; Thread.Sleep(10000)) 并查看您现在是否看到许多线程正在运行。您应该假设 hosts 确实包含许多工作项(到底有多少?)。
  • 确实,通过替换 CheckDevice(host, callback);睡觉我可以看到很多线程在睡觉
  • Liam,替换并发包的列表有效,请给出答案,我会接受它

标签: c# network-programming task-parallel-library


【解决方案1】:

您的问题是您正在迭代 List 的非线程安全项。

如果您将其替换为像ConcurrentBag 这样的线程安全对象,您应该会发现线程将并行运行。


我有点困惑,为什么它只运行一个线程,我相信是这行代码:

try
{
    ihe = Dns.Resolve(host);
}
catch (Exception)
{
    return;
}

我认为这是抛出异常并返回;因此你只看到一个线程。这也与您的观察有关,即如果您添加了睡眠,它会正常工作。

请记住,当您传递一个字符串时,您传递的是对内存中字符串的引用,而不是值。无论如何,ConcurrentBag 似乎可以解决您的问题。 This answer might also be relevant

【讨论】:

  • Dns.Resolve(host);超时后抛出异常,所以我仍然看到线程在将其替换为 ConcurrentBag 后停止在该行上,从测试中我会说 TPL 以某种方式检测到我正在从非线程安全结构访问成员并进行序列化确保可预测结果的任务。
  • 还有,Dns.Resolve(host);不是我用睡眠替换的那行,而是 CheckDevice(host, callback);这反过来又使主机变量变得未使用
  • 但是 CheckDevice 调用 Resolve?也解决是您使用字符串主机的唯一点。都是同一个问题。引用类型正在任务之外移动。这要么使 Resolve 失败,要么以不安全的方式更新 List。抛出的异常类型是什么?
  • 确实,CheckDevice 调用了 Resolve,异常是一个 SocketException,带有一条消息说请求的名称无效,但是当我替换 ihe = Dns.Resolve(host); for ihe = Dns.Resolve("无效主机");我仍然可以看到无效主机的并行解析,问题似乎只是访问主机变量,即使它没有更新
  • 所以 DNS.ResolveHost 不喜欢从外观上更新引用类型。我希望 SocketException 有一个(或几个)InnerException(s)?不要忘记字符串会不断更新,因为它在 foreach 中。我认为 ConsurrentBag 实际上打破了从 foreach 返回的值与集合中存储的值之间的引用。这就是更改列表类型可以解决问题的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-02
  • 1970-01-01
  • 2015-03-29
  • 2013-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多