首页 > 解决方案 > C# Windows 异步 Ping 网络 - 每次运行的结果不同

问题描述

我编写了一个异步 ping 子网的类。它可以工作,但是,返回的主机数量有时会在运行之间发生变化。一些问题:

ScanIPAddressesAsync()方法是这样调用的:

NetworkDiscovery nd = new NetworkDiscovery("192.168.50.");
nd.RaiseIPScanCompleteEvent += HandleScanComplete;
nd.ScanIPAddressesAsync();
namespace BPSTestTool
{

    public class IPScanCompleteEvent : EventArgs
    {
        public List<String> IPList { get; set; }

        public IPScanCompleteEvent(List<String> _list)
        {
            IPList = _list;
        }
    }

    public class NetworkDiscovery
    {
        private static object m_lockObj = new object();
        private List<String> m_ipsFound = new List<string>();
        private String m_ipBase = null;


        public List<String> IPList
        {
            get { return m_ipsFound; }
        }

        public EventHandler<IPScanCompleteEvent> RaiseIPScanCompleteEvent;

        public NetworkDiscovery(string ipBase)
        {
            this.m_ipBase = ipBase;
        }

        public async void ScanIPAddressesAsync()
        {
            var tasks = new List<Task>();

            m_ipsFound.Clear();
            await Task.Run(() => AsyncScan());
            return;
        }

        private async void AsyncScan()
        {
            List<Task> tasks = new List<Task>();
            for (int i = 2; i < 255; i++)
            {
                String ip = m_ipBase + i.ToString();

                if (m_ipsFound.Contains(ip) == false)
                {
                    for (int x = 0; x < 2; x++)
                    {
                        Ping p = new Ping();
                        var task = HandlePingReplyAsync(p, ip);
                        tasks.Add(task);
                    }
                }
            }
            await Task.WhenAll(tasks).ContinueWith(t =>
            {
                OnRaiseIPScanCompleteEvent(new IPScanCompleteEvent(m_ipsFound));
            });
        }

        protected virtual void OnRaiseIPScanCompleteEvent(IPScanCompleteEvent args)
        {
            RaiseIPScanCompleteEvent?.Invoke(this, args);
        }

        private async Task HandlePingReplyAsync(Ping ping, String ip)
        {
            PingReply reply = await ping.SendPingAsync(ip, 1500);

            if ( reply != null && reply.Status == System.Net.NetworkInformation.IPStatus.Success)
            {
                lock (m_lockObj)
                {
                    if (m_ipsFound.Contains(ip) == false)
                    {
                        m_ipsFound.Add(ip);
                    }
                }
            }
        }
    }
}

标签: c#asynchronousasync-awaitping

解决方案


我看到的一个问题是async void。甚至允许的唯一原因async void是仅用于事件处理程序。如果它不是事件处理程序,那就是危险信号。

异步方法总是开始同步运行,直到第一个await作用于不完整的Task. 在您的代码中,位于await Task.WhenAll(tasks). 此时,AsyncScan 返回- 在所有任务完成之前。通常,它会返回一个Task让你知道它何时完成的,但由于方法签名是void,它不能。

所以现在看这个:

await Task.Run(() => AsyncScan());

AsyncScan()返回时,Task返回的 fromTask.Run完成并且您的代码继续前进,在所有 ping 完成之前

因此,当您报告结果时,结果的数量将是随机的,具体取决于在您显示结果之前发生了多少。

如果您想确保在继续之前完成所有 ping 操作,请更改AsyncScan()为返回 a Task

private async Task AsyncScan()

并更改Task.Run等待它:

await Task.Run(async () => await AsyncScan());

但是,您也可以摆脱Task.Run并拥有以下内容:

await AsyncScan();

Task.Run在单独的线程中运行代码。这样做的唯一原因是在 UI 应用程序中,您希望将 CPU 繁重的计算从 UI 线程中移出。当你只是在做这样的网络请求时,这是没有必要的。

最重要的是,您还在async void这里使用:

public async void ScanIPAddressesAsync()

这意味着无论您打电话到哪里,ScanIPAddressesAsync()都无法等到一切都完成。将其更改为async Task并等待它。


推荐阅读