c# - 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);
}
}
}
}
}
}
解决方案
我看到的一个问题是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
并等待它。
推荐阅读
- python - 使用空格分割字符串并使用python将它们存储在列表中
- scala - 如何使用 Spark/Scala 将同一列中的不同值连接到一行中?
- google-sheets - 谷歌表格:将百万/十亿的价值转换为数字
- amazon-web-services - 带有应用程序负载均衡器的 Cloudfront 返回 504
- python-3.x - 如何将多个表单放在同一个views.py函数中 - Django
- python - scipy 中的 Basinhopping - 仅对某些维度使用假零导数/雅可比,对其他维度使用非零
- php - 从函数访问值
- php - 如何重构自定义 http 客户端类?
- heroku - 使用 Socket.IO 部署 GraphQL API
- javascript - 当我们在 JavaScript 中将原语视为对象时会发生什么?