C#使用Ping异步实现网络设备发现时如何等待所有线程结束
解决方案
方案1:使用CountdownEvent计数等待(适配你当前的EAP异步实现,无需大改原有逻辑)
核心逻辑
你发起N个Ping请求就初始化CountdownEvent的初始计数为N,每完成一个Ping请求就将计数减1,主线程阻塞等待计数归0,即代表所有异步请求全部处理完成。
同时需要补充ipList的并发锁,避免多线程写入导致的数据错乱/丢失。
修改步骤
- 新增静态成员:
// 原有静态列表 private static List<string> ipList = new List<string>(); // 计数同步对象 private static CountdownEvent _pingCountdown; // 列表写入锁 private static object _listLock = new object();
- 修改Main方法逻辑:
static void Main(string[] args) { // 执行逻辑获取子网掩码和本地地址,先计算出需要Ping的IP总数量,替换为你实际的数值 int totalPingCount = 254; _pingCountdown = new CountdownEvent(totalPingCount); // 用for循环递增IP地址 for (int i = 1; i <= totalPingCount; i++) { IPAddress ip_address = ...; // 替换为你生成IP的原有逻辑 PingAsync(ip_address); } // 等待所有Ping请求完成,代替原来的手动ReadLine _pingCountdown.Wait(); // 用完释放资源 _pingCountdown.Dispose(); Console.WriteLine("List of all devices found :\n"); ipList.ForEach(str => { Console.WriteLine($"\t=> {str}"); }); Console.ReadLine(); }
- 修改PingCompletedCallback的完成逻辑,补充计数减1和列表锁:
private static void PingCompletedCallback(object sender, PingCompletedEventArgs e) { try { if (e.Cancelled || e.Error != null) { Console.WriteLine("Ping failed !"); if(e.Error != null) Console.WriteLine(e.Error.ToString()); return; } PingReply reply = e.Reply; if(reply.Status == IPStatus.Success) { // 加锁保证线程安全 lock(_listLock) { ipList.Add(reply.Address.ToString()); } } } finally { // 无论成功失败,都触发计数减1 _pingCountdown.Signal(); ((AutoResetEvent)e.UserState)?.Dispose(); ((Ping)sender).Dispose(); } }
方案2:改用TAP异步模式(更简洁的现代写法)
如果允许调整PingAsync的实现,可以直接用Ping类自带的SendPingAsync方法,返回Task对象,直接用Task.WhenAll等待所有任务完成:
static async Task Main(string[] args) { List<Task<PingReply>> pingTasks = new List<Task<PingReply>>(); List<string> ipList = new List<string>(); // 替换为你生成的所有待Ping IP列表 List<IPAddress> allIps = ...; string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; byte[] buffer = Encoding.ASCII.GetBytes(data); int timeout = 1200; PingOptions options = new PingOptions(64, true); foreach (var ip in allIps) { pingTasks.Add(new Ping().SendPingAsync(ip, timeout, buffer, options)); } // 等待所有Ping完成 var results = await Task.WhenAll(pingTasks); foreach (var res in results) { if(res.Status == IPStatus.Success) { ipList.Add(res.Address.ToString()); } } // 打印列表 Console.WriteLine("List of all devices found :\n"); ipList.ForEach(str => Console.WriteLine($"\t=> {str}")); Console.ReadLine(); }
额外注意点
- 你原有代码里
PingCompletedCallback中实例化Program prog = new Program();是冗余代码,没有用到可以直接删掉 - 所有Ping和同步对象用完要主动释放,避免内存泄漏
内容的提问来源于stack exchange,提问作者FrougeurPro
相关产品推荐
相关产品推荐

