我正在開發一個“心跳”應用程式,它通過回圈每分鐘 ping 數百個 IP 地址。IP 地址存盤在一個類的串列中Machines。我有一個回圈,它為每個 IP創建一個Task<MachinePingResults>(MachinePingResults基本上是一個 IP 和在線狀態的元組),并使用System.Net.NetworkInformation.
我遇到的問題是,在運行數小時(或數天)后,主程式的一個回圈無法完成Tasks,導致記憶體泄漏。我無法確定為什么我的任務沒有完成(如果我在運行幾天后在運行時查看任務串列,有數百個任務顯示為“等待”)。大部分時間所有的任務都完成并被處理;他們沒有完成只是隨機的。例如,過去 24 小時在大約 12 小時有一個問題,有 148 個等待任務從未完成。由于無法看到Ping掛起原因的性質(因為它是 .NET 內部的),我無法復制該問題以進行除錯。
(如果存在 DNS 問題,似乎Ping.NET中的呼叫可能會掛起并且內置超時失敗,這就是我內置額外超時的原因)
我有一個辦法取消主回圈如果ping不使用15秒內恢復Task.Delay和CancellationToken。然后在每個 Ping 函式中,我有一個Delay以防 Ping 呼叫本身掛起,從而強制函式完成。另請注意,我只是在 ping IPv4;沒有 IPv6 或 URL。
主回圈
pingcancel = new CancellationTokenSource();
List<Task<MachinePingResults>> results = new List<Task<MachinePingResults>>();
try
{
foreach (var m in localMachines.FindAll(m => !m.Online))
results.Add(Task.Run(() =>
PingMachine(m.ipAddress, 8000), pingcancel.Token
));
await Task.WhenAny(Task.WhenAll(results.ToArray()), Task.Delay(15000));
pingcancel.Cancel();
}
catch (Exception ex) { Console.WriteLine(ex); }
finally
{
results.Where(r => r.IsCompleted).ToList()
.ForEach(r =>
//modify the online machines);
results.Where(r => r.IsCompleted).ToList().ForEach(r => r.Dispose());
results.Clear();
}
Ping 功能
static async Task<MachinePingResults> PingMachine(string ipaddress, int timeout)
{
try
{
using (Ping ping = new Ping())
{
var reply = ping.SendPingAsync(ipaddress, timeout);
await Task.WhenAny(Task.Delay(timeout), reply);
if (reply.IsCompleted && reply.Result.Status == IPStatus.Success)
{
return new MachinePingResults(ipaddress, true);
}
}
}
catch (Exception ex)
{
Debug.WriteLine("Error: " ex.Message);
}
return new MachinePingResults(ipaddress, false);
}
Task如果 Ping 掛起,每個都有延遲讓它繼續,我不知道是什么問題導致了一些Task<MachinePingResults>永遠無法完成。
如何確保Task使用 .NETPing結束?
Using .NET 5.0 and the issues occurs on machines running windows 10 and windows server 2012

uj5u.com熱心網友回復:
發布的代碼中存在不少空白,但我嘗試復制并最終進行了一些重構。
這個版本看起來非常健壯,實際呼叫SendAsync包裝在一個配接器類中。
我接受這不一定直接回答問題,但是在無法準確復制您的問題的情況下,提供了一種可以消除問題的結構化代碼的替代方法。
async Task Main()
{
var masterCts = new CancellationTokenSource(TimeSpan.FromSeconds(15)); // 15s overall timeout
var localMachines = new List<LocalMachine>
{
new LocalMachine("192.0.0.1", false), // Should be not known - TimedOut
new LocalMachine("192.168.86.88", false), // Should be not known - DestinationHostUnreachable (when timeout is 8000)
new LocalMachine("www.dfdfsdfdfdsgrdf.cdcc", false), // Should be not known - status Unknown because of PingException
new LocalMachine("192.168.86.87", false) // Known - my local IP
};
var results = new List<PingerResult>();
try
{
// Create the "hot" tasks
var tasks = localMachines.Where(m => !m.Online)
.Select(m => new Pinger().SendPingAsync(m.HostOrAddress, 8000, masterCts.Token))
.ToArray();
await Task.WhenAll(tasks);
results.AddRange(tasks.Select(t => t.Result));
}
finally
{
results.ForEach(r => localMachines.Single(m => m.HostOrAddress.Equals(r.HostOrAddress)).Online = r.Status == IPStatus.Success);
results.Dump(); // For LINQPad
localMachines.Dump(); // For LINQPad
results.Clear();
}
}
public class LocalMachine
{
public LocalMachine(string hostOrAddress, bool online)
{
HostOrAddress = hostOrAddress;
Online = online;
}
public string HostOrAddress { get; }
public bool Online { get; set; }
}
public class PingerResult
{
public string HostOrAddress {get;set;}
public IPStatus Status {get;set;}
}
public class Pinger
{
public async Task<PingerResult> SendPingAsync(string hostOrAddress, int timeout, CancellationToken token)
{
// Check if timeout has occurred
token.ThrowIfCancellationRequested();
IPStatus status = default;
try
{
var reply = await SendPingInternal(hostOrAddress, timeout, token);
status = reply.Status;
}
catch (PingException)
{
status = IPStatus.Unknown;
}
return new PingerResult
{
HostOrAddress = hostOrAddress,
Status = status
};
}
// Wrap the legacy EAP pattern offered by Ping.
private Task<PingReply> SendPingInternal(string hostOrAddress, int timeout, CancellationToken cancelToken)
{
var tcs = new TaskCompletionSource<PingReply>();
if (cancelToken.IsCancellationRequested)
{
tcs.TrySetCanceled();
}
else
{
using (var ping = new Ping())
{
ping.PingCompleted = (object sender, PingCompletedEventArgs e) =>
{
if (!cancelToken.IsCancellationRequested)
{
if (e.Cancelled)
{
tcs.TrySetCanceled();
}
else if (e.Error != null)
{
tcs.TrySetException(e.Error);
}
else
{
tcs.TrySetResult(e.Reply);
}
}
};
cancelToken.Register(() => { tcs.TrySetCanceled(); });
ping.SendAsync(hostOrAddress, timeout, new object());
}
};
return tcs.Task;
}
}
編輯:
我剛剛在評論中注意到您提到 ping “所有 1391”。在這一點上,我會考慮使用SemaphoreSlim. 請參閱這篇博客文章(很久以前!),其中概述了該方法:https : //devblogs.microsoft.com/pfxteam/implementing-a-simple-foreachasync/
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/369851.html
標籤:c# asynchronous .net-5 ping
