问题:我们有一些IP地址,然后我们应该按用户指定的时间段ping每一个,例如每400ms ping 192.168.0.1,每40000ms ping 192.168.137.20等等...我们可以以异步方式处理这种情况吗?
这是我从数据库加载目标信息并为每个人创建传感器的地方:
public class PingService
{
private CancellationTokenSource _cancel;
private List<PingSensor> _pings;
private IRepository<IPDevice> _deviceRepository;
public PingService()
{
_cancel = new CancellationTokenSource();
_pings = new List<PingSensor>();
_deviceRepository = ObjectFactory.GetInstance<IRepository<IPDevice>>();
}
public void Start()
{
Action act = new Action(() =>
{
IQueryable<IPDevice> allDevices = _deviceRepository.GetAll();
foreach (IPDevice device in allDevices)
{
PingResultCollector collector = new PingResultCollector(device);
_pings.Add(new PingSensor(device.Address, collector, device.CheckDuration, _cancel.Token));
}
foreach (PingSensor _ping in _pings)
{
_ping.DoDiscovery();
}
});
Task.Factory.StartNew(act);
}
public void Stop()
{
_cancel.Cancel();
}
}
这是我们ping的地方,等待用户指定的延迟,收集响应和......
public class PingSensor
{
private string _address;
Ping _ping;
private bool lastRequestReplayed;
private int _delay;
private CancellationToken _cancellationToken;
private PingResultCollector _resultCollector;
public PingSensor(string address, PingResultCollector resultCollector, int delay, CancellationToken CancellationToken)
{
_address = address;
_cancellationToken = CancellationToken;
_ping = new Ping();
_resultCollector = resultCollector;
_ping.PingCompleted += _ping_PingCompleted;
_delay = delay;
}
void _ping_PingCompleted(object sender, PingCompletedEventArgs e)
{
if (_cancellationToken.IsCancellationRequested)
return;
lastRequestReplayed = true;
//_eventHandler(sender, e);
if (_resultCollector != null)
_resultCollector.CollectData(new PingStatus()
{
PingStatusId = Guid.NewGuid(),
Address = e.Reply == null ? "" : e.Reply.Address.ToString(),
Status = e.Reply == null ? e.Error.Message : e.Reply.Status.ToString(),
Target = e.UserState.ToString(),
ResponseTime = e.Reply == null ? 0 : e.Reply.RoundtripTime,
UpdateTime = DateTime.UtcNow
});
}
public void DoDiscovery()
{
lastRequestReplayed = true;
Action act = new Action(() =>
{
while (_cancellationToken.IsCancellationRequested!=true)
{
if (_cancellationToken.IsCancellationRequested)
return;
byte[] data = new byte[2];
if (lastRequestReplayed)
{
_ping.SendAsync(_address, 30000, data, _address);
lastRequestReplayed = false;
}
if (_cancellationToken.IsCancellationRequested)
return;
Thread.Sleep(_delay);
if (_cancellationToken.IsCancellationRequested)
return;
}
});
Task.Factory.StartNew(act);
}
}
这个代码的问题是它为每个传感器创建一个线程,这意味着如果我有500个目标来ping我也有500个线程!有什么建议吗?抱歉我的英语不好:D
答案 0 :(得分:1)
我可以用工作代码示例来演示这个概念..由你来转换成适合你的场景的代码100%
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
// Pick your long list of ip address
List<IPAddress> ips = new List<IPAddress>
{
new IPAddress(new byte[] {127, 0, 0, 1}),
new IPAddress(new byte[] {198, 252, 206, 16}),
new IPAddress(new byte[] {74, 125, 129, 99}),
// Add more ips as you like
};
// Exactly what do you do with initiated tasks will depend on your specific scenario.
List<Task> tps = new List<Task>();
foreach(var ip in ips)
{
// Delay could vary by IP, but I am hardcoding 10s here.
tps.Add(InitiatePings(ip, 10000));
}
// Needed so that console app doesn't exit..
Console.ReadLine();
}
private static async Task InitiatePings(IPAddress ip, int delay)
{
while (true)
{
// Note, this API is different from SendAsync API you are using
// You may also want to reuse Ping instance instead of creating new one each time.
var result = await new Ping().SendPingAsync(ip);
// Process your result here, however you want.
Console.WriteLine(result.Address + "-" + result.Status + "-" + result.RoundtripTime);
// Assumes that the delay is not absolute, but time between ping, result processing and next ping.
await Task.Delay(delay);
}
}
}
}
答案 1 :(得分:1)
为什么不为每个要ping的IP地址设置Timer?那就是:
List<System.Threading.Timer> timers = new List<System.Threading.Timer>();
您可以使用以下内容添加各个计时器:
timers.Add(new Timer(PingTimerProc, "192.168.1.1", 400, 400));
timers.Add(new Timer(PingTimerProc, "10.10.200.50", 42000, 42000));
// add other timers
你的计时器proc:
void PingTimerProc(object state)
{
string ipAddress = (string)state;
// do the ping here
}
这里唯一的潜在缺点是,如果需要非常频繁地ping通足够多的IP地址,则存在无法足够快地处理所有计时器事件的风险。这最终会导致颠簸,因为你会有一堆待定的线程。
如果你用一个线程执行此操作并且在如此短的时间间隔内有很多ping,那么你就会开始落后。它不会最终杀死你的过程。
如果我使用单个线程执行此操作,我将创建IP地址的优先级队列及其关联的ping时间。并且长时间运行的任务设置为该队列提供服务。基本结构将是这样的:
class PingJob
{
string IPAddress;
int Period; // milliseconds between pings
TimeSpan NextPingTime;
}
var queue = new PriorityQueue<PingJob>();
// create the ping jobs and add them to the queue
queue.Add(new PingJob { IPAddress = "192.168.1.1", Period = 400, NextPingTime = TimeSpan.FromMilliseconds(400) });
queue.Add(new PingJob { IPAddress = "10.10.200.50", Period = 42000, NextPingTime = TimeSpan.FromMilliseconds(42000) });
// etc.
// start a thread that processes the pings
var pingTask = Task.Factory.StartNew(PingTaskProc, TaskCreationOptions.LongRunning);
任务处理:
void PingTaskProc()
{
var pingTimer = Stopwatch.StartNew();
while (true)
{
var p = queue.Dequeue(); // get ping job
if (p.NextPingTime > pingTimer.Elapsed)
{
// wait for ping time to come up
Thread.Sleep(p.NextPingTime - pingTimer.Elapsed);
}
// ping the IP address
DoPing(p.IPAddress);
// update its next ping time
p.NextPingTime += TimeSpan.FromMilliseconds(p.Period);
// And add it back to the queue
queue.Add(p);
}
}
假设您具有可用的通用优先级队列数据结构。有许多可用的,包括mine。
如果您可以从队列中添加和删除IP地址,或更改其ping周期,那么事情会变得更有趣。但你没有指明这一点。