C# Windows 异步 Pinging 网络 - 每个结果不同 运行
C# Windows Async Pinging Network - different results each run
我写了一个 class 异步 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);
}
}
}
}
}
}
此代码需要进行大量重构,并且像这样的并发错误很难查明。我的赌注是 await Task.Run(() => AsyncScan());
,这是错误的,因为 AsyncScan()
是 async
,而 Task.Run(...)
将 return 完成。
我的第二个猜测是 m_ipsFound
,它被称为 共享状态 。这意味着可能有许多线程同时读取和写入它。 List<T>
不是此数据类型。
此外,在方法的最后一行有一个 return
的侧点不会增加可读性,并且 async void
是一种被禁止的做法。永远使用 async Task
即使你 return 什么都没有。您可以阅读更多关于 this 非常好的答案。
我看到的一个问题是 async void
。甚至允许 async void
的唯一原因仅适用于事件处理程序。如果它不是事件处理程序,它就是一个危险信号。
异步方法始终同步启动 运行,直到第一个 await
作用于不完整的 Task
。在您的代码中,即 await Task.WhenAll(tasks)
。那时,AsyncScan
returns - 在所有任务完成之前。通常,它会 return 一个 Task
让你知道它何时完成,但由于方法签名是 void
,它不能。
所以现在看这个:
await Task.Run(() => AsyncScan());
当 AsyncScan()
return 时,Task
return 从 Task.Run
完成并且您的代码继续,之前所有 ping 都已完成。
因此,当您报告结果时,结果的数量将是随机的,具体取决于在显示结果之前碰巧完成了多少。
如果您想确保在继续之前完成所有 ping,请将 AsyncScan()
更改为 return 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
并等待它。
我写了一个 class 异步 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);
}
}
}
}
}
}
此代码需要进行大量重构,并且像这样的并发错误很难查明。我的赌注是 await Task.Run(() => AsyncScan());
,这是错误的,因为 AsyncScan()
是 async
,而 Task.Run(...)
将 return 完成。
我的第二个猜测是 m_ipsFound
,它被称为 共享状态 。这意味着可能有许多线程同时读取和写入它。 List<T>
不是此数据类型。
此外,在方法的最后一行有一个 return
的侧点不会增加可读性,并且 async void
是一种被禁止的做法。永远使用 async Task
即使你 return 什么都没有。您可以阅读更多关于 this 非常好的答案。
我看到的一个问题是 async void
。甚至允许 async void
的唯一原因仅适用于事件处理程序。如果它不是事件处理程序,它就是一个危险信号。
异步方法始终同步启动 运行,直到第一个 await
作用于不完整的 Task
。在您的代码中,即 await Task.WhenAll(tasks)
。那时,AsyncScan
returns - 在所有任务完成之前。通常,它会 return 一个 Task
让你知道它何时完成,但由于方法签名是 void
,它不能。
所以现在看这个:
await Task.Run(() => AsyncScan());
当 AsyncScan()
return 时,Task
return 从 Task.Run
完成并且您的代码继续,之前所有 ping 都已完成。
因此,当您报告结果时,结果的数量将是随机的,具体取决于在显示结果之前碰巧完成了多少。
如果您想确保在继续之前完成所有 ping,请将 AsyncScan()
更改为 return 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
并等待它。