程序需要在1秒钟内ping千多台机器,然后将结果写入日志…由于网络问题,会出现有些机器ping不同,用循环来做就会阻塞,于是选了用异步Ping.SendAsync来做。这个代码其实工作的挺好。然而,程序顺利工作半天以后,我被公司网管部门盯上了,原因是部署了这个程序的服务器,偶尔会CPU上到100%(大约5~10分钟一次)。而常态下,这个程序的CPU使用是在20%左右(其实就是两个线程,一个用来产生异步ping,一个用来处理队列中的日志)
我抓了dump,结合代码分析了一下原因。某些时候,这些异步任务的回调PingCompleted会集中在一个时间点同时结束,这个时候,回调的线程大约有80多个,同时执行时CPU会瞬间飚升到100%。
这种情况下,有什么思路,能避免CPU瞬间飚升到100%,而让cpu能够稳定在20%?
代码大致如下:
public void PingList()
{
foreach (var ip in _ipList)
{
try
{
PingAsnc(ip);
}
catch
{
}
}
}
static byte[] data = Encoding.ASCII.GetBytes("12345678901234567890123456789012");
public void PingAsnc(string ip)
{
Ping ping = new Ping();
PingOptions options = new PingOptions();
options.DontFragment = true;
int timeout = 20;
ping.PingCompleted += PingCompleted;
ping.SendAsync(ping.IP, timeout, data, options, null);
}
public void PingCompleted(object sender, PingCompletedEventArgs e)
{
PingReply reply = e.Reply;
//将reply加入一个无锁队列,用以写日志balabalabala。
}
解决方案:50分
解决方案:10分
PingCompleted里边的方法用Control.Invoke搞到主线程去做,这样永远只用一个核,在4核CPU上占用率就是25%再或者限制同时ping的数量,比如100个:
Task.Factory.StartNew(() =>
{
_ipList.AsParallel().WithDegreeOfParallelism(100).ForAll((i) =>
{
var ping = new Ping();
//xxxx
});
});
Task.Factory.StartNew(() =>
{
_ipList.AsParallel().WithDegreeOfParallelism(100).ForAll((i) =>
{
var ping = new Ping();
//xxxx
});
});
解决方案:40分
Thread.Sleep(0)在这种需求下可能不合适,你最好是在PingCompleted中处理队列的地方使用lock串行处理。