You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C# .NET 4.6.2中高效实现多设备不同间隔Ping检测

优化多设备Ping检测的实现方案(.NET 4.6.2)

你的当前方案虽然简单,但当设备数量较多时,每秒遍历所有设备会产生不必要的计算开销。下面是两种更高效的实现方式,适配.NET 4.6.2的特性:

方案一:为每个设备单独维护异步定时任务

每个设备独立管理自己的Ping周期,使用async/await结合Task.Delay实现非阻塞的定时检测,避免全局遍历的开销。

核心代码示例:

public class DeviceMonitor
{
    private readonly Device _device;
    private readonly Ping _ping;
    private CancellationTokenSource _cts;
    private bool _isRunning;

    public DeviceMonitor(Device device)
    {
        _device = device;
        _ping = new Ping();
    }

    public async Task StartMonitoring()
    {
        if (_isRunning) return;
        _isRunning = true;
        _cts = new CancellationTokenSource();

        try
        {
            while (!_cts.Token.IsCancellationRequested)
            {
                // 执行Ping检测
                var reply = await _ping.SendPingAsync(_device.IpAddress, 1000);
                _device.IsOnline = reply.Status == IPStatus.Success;
                _device.LastPingTime = DateTime.Now;

                // 根据设备配置的间隔延迟
                await Task.Delay(_device.PingInterval * 1000, _cts.Token);
            }
        }
        catch (TaskCanceledException)
        {
            // 任务取消,正常退出
        }
        finally
        {
            _isRunning = false;
            _ping.Dispose();
        }
    }

    public void StopMonitoring()
    {
        _cts?.Cancel();
    }
}

// 设备模型示例
public class Device
{
    public string IpAddress { get; set; }
    public int PingInterval { get; set; } // 单位:秒
    public bool IsOnline { get; set; }
    public DateTime LastPingTime { get; set; }
}

优势:

  • 实现简单,每个设备的逻辑独立,易于维护
  • 无全局遍历开销,资源利用率高
  • 异步操作不会阻塞UI线程或主线程

注意事项:

  • 需为每个设备创建独立的DeviceMonitor实例,启动/停止时统一管理
  • 状态更新到UI时,需通过Dispatcher.Invoke(WPF)或Control.Invoke(WinForms)确保线程安全

方案二:使用优先级队列调度Ping任务

当设备数量较多时,维护一个按「下一次Ping时间」排序的优先级队列,仅在到达指定时间时处理对应设备,避免无效遍历。由于.NET 4.6.2没有内置的PriorityQueue,可以用SortedDictionary实现类似功能。

核心代码示例:

public class PingScheduler
{
    private readonly SortedDictionary<DateTime, List<Device>> _scheduleQueue = new SortedDictionary<DateTime, List<Device>>();
    private readonly Ping _ping;
    private CancellationTokenSource _cts;
    private Task _schedulerTask;
    private readonly object _lockObj = new object();

    public PingScheduler()
    {
        _ping = new Ping();
    }

    public void AddDevice(Device device)
    {
        lock (_lockObj)
        {
            var nextPingTime = DateTime.Now.AddSeconds(device.PingInterval);
            if (!_scheduleQueue.ContainsKey(nextPingTime))
            {
                _scheduleQueue[nextPingTime] = new List<Device>();
            }
            _scheduleQueue[nextPingTime].Add(device);
        }
    }

    public void RemoveDevice(Device device)
    {
        lock (_lockObj)
        {
            foreach (var entry in _scheduleQueue.ToList())
            {
                entry.Value.Remove(device);
                if (entry.Value.Count == 0)
                {
                    _scheduleQueue.Remove(entry.Key);
                }
            }
        }
    }

    public async Task StartScheduling()
    {
        if (_schedulerTask != null && !_schedulerTask.IsCompleted) return;
        _cts = new CancellationTokenSource();
        _schedulerTask = RunSchedulerLoop(_cts.Token);
    }

    private async Task RunSchedulerLoop(CancellationToken token)
    {
        while (!token.IsCancellationRequested)
        {
            Device deviceToPing = null;
            DateTime nextPingTime = DateTime.MaxValue;

            lock (_lockObj)
            {
                if (_scheduleQueue.Any())
                {
                    var firstEntry = _scheduleQueue.First();
                    nextPingTime = firstEntry.Key;
                    deviceToPing = firstEntry.Value.First();
                    firstEntry.Value.Remove(deviceToPing);
                    if (firstEntry.Value.Count == 0)
                    {
                        _scheduleQueue.Remove(firstEntry.Key);
                    }
                }
            }

            if (deviceToPing == null)
            {
                await Task.Delay(1000, token);
                continue;
            }

            // 计算等待时间,避免提前执行
            var delayMs = (nextPingTime - DateTime.Now).TotalMilliseconds;
            if (delayMs > 0)
            {
                await Task.Delay((int)delayMs, token);
            }

            // 执行Ping检测
            try
            {
                var reply = await _ping.SendPingAsync(deviceToPing.IpAddress, 1000);
                deviceToPing.IsOnline = reply.Status == IPStatus.Success;
                deviceToPing.LastPingTime = DateTime.Now;
            }
            catch
            {
                deviceToPing.IsOnline = false;
            }

            // 将设备重新加入队列,计算下一次Ping时间
            lock (_lockObj)
            {
                var newNextPingTime = DateTime.Now.AddSeconds(deviceToPing.PingInterval);
                if (!_scheduleQueue.ContainsKey(newNextPingTime))
                {
                    _scheduleQueue[newNextPingTime] = new List<Device>();
                }
                _scheduleQueue[newNextPingTime].Add(deviceToPing);
            }
        }
    }

    public void StopScheduling()
    {
        _cts?.Cancel();
        _ping.Dispose();
    }
}

优势:

  • 仅在需要Ping时才处理设备,完全避免无效遍历
  • 适合大量设备的场景,资源占用更低

注意事项:

  • 队列操作需加锁确保线程安全(因为可能有添加/移除设备的操作)
  • 需处理设备Ping间隔修改的场景(移除旧的队列条目,添加新的)

方案选择建议

  • 设备数量较少(几十台以内):优先选方案一,实现简单,维护成本低
  • 设备数量较多(上百台或更多):选方案二,能显著降低系统开销

内容的提问来源于stack exchange,提问作者Hem Bhagat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 08:25:16