サーバーのコレクションの平均ラウンドトリップ時間を計算しようとしています。処理を高速化するために、pingを並行して実行したいと思います。 AverageRoundtripTime()
という関数を書いたのですが、うまくいくようですが、マルチスレッドについてはよくわからないので、大丈夫かと思います。私のコードを見て、それが大丈夫かどうか、または私が望むことを達成するためのより良い方法があるかどうかを私に知らせてください:
public void Main()
{
// Collection of hosts.
List<String> hosts = new List<String>();
// Add 100 hosts to the collection.
for (Int32 i = 0; i < 100; ++i) hosts.Add("www.google.com");
// Display the average round-trip time for 100 hosts.
Console.WriteLine(AverageRoundtripTime(hosts));
}
public Double AverageRoundtripTime(IEnumerable<String> hosts)
{
// Collection of threads.
List<Thread> threads = new List<Thread>();
// Collection of ping replies.
List<PingReply> pingReplies = new List<PingReply>();
// Loop through all Host names.
foreach (var Host in hosts)
{
// Create a new thread.
Thread thread = new Thread(() =>
{
// Variable to hold the ping reply.
PingReply reply = null;
// Create a new Ping object and make sure that it's
// disposed after we're finished with it.
using (Ping ping = new Ping())
{
reply = ping.Send(Host);
}
// Get exclusive lock on the pingReplies collection.
lock (pingReplies)
{
// Add the ping reply to the collection.
pingReplies.Add(reply);
}
});
// Add the newly created thread to the theads collection.
threads.Add(thread);
// Start the thread.
thread.Start();
}
// Wait for all threads to complete
foreach (Thread thread in threads)
{
thread.Join();
}
// Calculate and return the average round-trip time.
return pingReplies.Average(x => x.RoundtripTime);
}
更新:
私が尋ねた関連する質問をチェックしてください:
タスク並列ライブラリコードがWindowsフォームアプリケーションでフリーズする-Windowsコンソールアプリケーションとして正常に機能する
PingクラスにはメソッドSendAsync
があります。これは、イベントベースの非同期プログラミング(EAP)パターンに従います。この記事をチェックしてください: http://msdn.Microsoft.com/en-us/library/ee622454.aspx 。
簡単な例として、非常に基本的な方法でその記事を実装する方法があります。基本的にこれは何度でも呼び出すことができ、すべてのpingは非同期で実行されます。
class Program
{
public static string[] addresses = {"Microsoft.com", "yahoo.com", "google.com"};
static void Main(string[] args)
{
List<Task<PingReply>> pingTasks = new List<Task<PingReply>>();
foreach (var address in addresses)
{
pingTasks.Add(PingAsync(address));
}
//Wait for all the tasks to complete
Task.WaitAll(pingTasks.ToArray());
//Now you can iterate over your list of pingTasks
foreach (var pingTask in pingTasks)
{
//pingTask.Result is whatever type T was declared in PingAsync
Console.WriteLine(pingTask.Result.RoundtripTime);
}
Console.ReadLine();
}
static Task<PingReply> PingAsync(string address)
{
var tcs = new TaskCompletionSource<PingReply>();
Ping ping = new Ping();
ping.PingCompleted += (obj, sender) =>
{
tcs.SetResult(sender.Reply);
};
ping.SendAsync(address, new object());
return tcs.Task;
}
}
parallel.ForとConcurrentBagを使用します
static void Main(string[] args)
{
Console.WriteLine(AverageRoundTripTime("www.google.com", 100));
Console.WriteLine(AverageRoundTripTime("www.stackoverflow.com", 100));
Console.ReadKey();
}
static double AverageRoundTripTime(string Host, int sampleSize)
{
ConcurrentBag<double> values = new ConcurrentBag<double>();
Parallel.For(1, sampleSize, (x, y) => values.Add(Ping(Host)));
return values.Sum(x => x) / sampleSize;
}
static double Ping(string Host)
{
var reply = new Ping().Send(Host);
if (reply != null)
return reply.RoundtripTime;
throw new Exception("denied");
}
// LINQを使用するとソリューションが簡単になります
List<String> hosts = new List<String>();
for (Int32 i = 0; i < 100; ++i) hosts.Add("www.google.com");
var average = hosts.AsParallel().WithDegreeOfParallelism(64).
Select(h => new Ping().Send(h).RoundtripTime).Average();
Console.WriteLine(average)
たぶん SendPingAsync このように使用します:
using (var ping = new Ping())
{
var replies = await Task.WhenAll(hosts.Select(x => ping.SendPingAsync(x)))
.ConfigureAwait(false);
// false here ^ unless you want to schedule back to sync context
... process replies.
}
解決策:
internal class Utils
{
internal static PingReply Ping (IPAddress address, int timeout = 1000, int ttl = 64)
{
PingReply tpr = null;
var p = new Ping ();
try {
tpr = p.Send (address,
timeout,
Encoding.ASCII.GetBytes ("oooooooooooooooooooooooooooooooo"),
new PingOptions (ttl, true));
} catch (Exception ex) {
tpr = null;
} finally {
if (p != null)
p.Dispose ();
p = null;
}
return tpr;
}
internal static List<PingReply> PingAddresses (List<IPAddress> addresses, int timeout = 1000, int ttl = 64)
{
var ret = addresses
.Select (p => Ping (p, timeout, ttl))
.Where (p => p != null)
.Where (p => p.Status == IPStatus.Success)
.Select (p => p).ToList ();
return ret;
}
internal static Task PingAddressesAsync (List<IPAddress> addresses, Action<Task<List<PingReply>>> endOfPing, int timeout = 1000, int ttl = 64)
{
return Task.Factory.StartNew<List<PingReply>> (() => Utils.PingAddresses (
addresses, timeout, ttl)).ContinueWith (t => endOfPing (t));
}
}
そして使用する:
Console.WriteLine ("start");
Utils.PingAddressesAsync (new List<IPAddress> () {
IPAddress.Parse ("192.168.1.1"),
IPAddress.Parse ("192.168.1.13"),
IPAddress.Parse ("192.168.1.49"),
IPAddress.Parse ("192.168.1.200")
}, delegate(Task<List<PingReply>> tpr) {
var lr = tpr.Result;
Console.WriteLine ("finish with " + lr.Count.ToString () + " machine found");
foreach (var pr in lr) {
Console.WriteLine (pr.Address.ToString ());
}
});
Console.WriteLine ("execute");
Console.ReadLine ();
これは、複数のエンドポイントにpingを実行できる非同期ワーカーです。ハートビートワーカーをStart()またはStop()して、次のイベントをサブスクライブできます。
-
public class NetworkHeartbeat
{
private static object lockObj = new object();
public bool Running { get; private set; }
public int PingTimeout { get; private set; }
public int HeartbeatDelay { get; private set; }
public IPAddress[] EndPoints { get; private set; }
public int Count => EndPoints.Length;
public PingReply[] PingResults { get; private set; }
private Ping[] Pings { get; set; }
public NetworkHeartbeat(IEnumerable<IPAddress> hosts, int pingTimeout, int heartbeatDelay)
{
PingTimeout = pingTimeout;
HeartbeatDelay = heartbeatDelay;
EndPoints = hosts.ToArray();
PingResults = new PingReply[EndPoints.Length];
Pings = EndPoints.Select(h => new Ping()).ToArray();
}
public async void Start()
{
if (!Running)
{
try
{
Debug.WriteLine("Heartbeat : starting ...");
// set up the tasks
var chrono = new Stopwatch();
var tasks = new Task<PingReply>[Count];
Running = true;
while (Running)
{
// set up and run async ping tasks
OnPulseStarted(DateTime.Now, chrono.Elapsed);
chrono.Restart();
for (int i = 0; i < Count; i++)
{
tasks[i] = PingAndUpdateAsync(Pings[i], EndPoints[i], i);
}
await Task.WhenAll(tasks);
for (int i = 0; i < tasks.Length; i++)
{
var pingResult = tasks[i].Result;
if (pingResult != null)
{
if (PingResults[i] == null)
{
if (pingResult.Status == IPStatus.Success)
OnPingUp(i);
}
else if (pingResult.Status != PingResults[i].Status)
{
if (pingResult.Status == IPStatus.Success)
OnPingUp(i);
else if (PingResults[i].Status == IPStatus.Success)
OnPingDown(i);
}
}
else
{
if (PingResults[i] != null && PingResults[i].Status == IPStatus.Success)
OnPingUp(i);
}
PingResults[i] = tasks[i].Result;
Debug.WriteLine("> Ping [" + PingResults[i].Status.ToString().ToUpper() + "] at " + EndPoints[i] + " in " + PingResults[i].RoundtripTime + " ms");
}
OnPulseEnded(DateTime.Now, chrono.Elapsed);
// heartbeat delay
var delay = Math.Max(0, HeartbeatDelay - (int)chrono.ElapsedMilliseconds);
await Task.Delay(delay);
}
Debug.Write("Heartbeat : stopped");
}
catch (Exception)
{
Debug.Write("Heartbeat : stopped after error");
Running = false;
throw;
}
}
else
{
Debug.WriteLine("Heartbeat : already started ...");
}
}
public void Stop()
{
Debug.WriteLine("Heartbeat : stopping ...");
Running = false;
}
private async Task<PingReply> PingAndUpdateAsync(Ping ping, IPAddress epIP, int epIndex)
{
try
{
return await ping.SendPingAsync(epIP, PingTimeout);
}
catch (Exception ex)
{
Debug.Write("-[" + epIP + "] : error in SendPing()");
OnPingError(epIndex, ex);
return null;
}
}
// Event on ping errors
public event EventHandler<PingErrorEventArgs> PingError;
public class PingErrorEventArgs : EventArgs
{
public int EndPointIndex { get; private set; }
public Exception InnerException { get; private set; }
public PingErrorEventArgs(int epIndex, Exception ex)
{
EndPointIndex = epIndex;
InnerException = ex;
}
}
private void OnPingError(int epIndex, Exception ex) => PingError?.Invoke(this, new PingErrorEventArgs(epIndex, ex));
// Event on ping Down
public event EventHandler<int> PingDown;
private void OnPingDown(int epIndex)
{
Debug.WriteLine("# Ping [DOWN] at " + EndPoints[epIndex]);
PingDown?.Invoke(this, epIndex);
}
// Event on ping Up
public event EventHandler<int> PingUp;
private void OnPingUp(int epIndex)
{
Debug.WriteLine("# Ping [UP] at " + EndPoints[epIndex] );
PingUp?.Invoke(this, epIndex);
}
// Event on Pulse started
public event EventHandler<PulseEventArgs> PulseStarted;
public class PulseEventArgs : EventArgs
{
public DateTime TimeStamp { get; private set; }
public TimeSpan Delay { get; private set; }
public PulseEventArgs(DateTime date, TimeSpan delay)
{
TimeStamp = date;
Delay = delay;
}
}
private void OnPulseStarted(DateTime date, TimeSpan delay)
{
Debug.WriteLine("# Heartbeat [Pulse START] after " + (int)delay.TotalMilliseconds + " ms");
PulseStarted?.Invoke(this, new PulseEventArgs(date, delay));
}
// Event on Pulse ended
public event EventHandler<PulseEventArgs> PulseEnded;
private void OnPulseEnded(DateTime date, TimeSpan delay)
{
PulseEnded?.Invoke(this, new PulseEventArgs(date, delay));
Debug.WriteLine("# Heartbeat [Pulse END] after " + (int)delay.TotalMilliseconds + " ms");
}
}