问题
I want to check the instrument connectivity, continuously. These instruments are connected by LAN.
I ping them in an infinite loop. After a few seconds, I'm met with a blue screen and the system restarts.
private async void checkingDevice()
{
await Task.Run(() =>
{
do
{
Panel selectedPanel;
multicastPing.send();
foreach (var device in (Device[])Enum.GetValues(typeof(Device)))
{
selectedPanel = (Panel)this.Controls.Find((device).ToString(), true)[0];
if (multicastPing.check(device.ToString()))
selectedPanel.BackgroundImage = Image.FromFile(Configs.imagesUrl + "enable\\" + selectedPanel.AccessibleName + ".png");
else
selectedPanel.BackgroundImage = Image.FromFile(Configs.imagesUrl + "disable\\" + selectedPanel.AccessibleName + ".png");
}
} while (!flag);
// TODO
// delete instrument object after using in this snippet code
});
}
public bool send()
{
foreach (var device in (Device[])Enum.GetValues(typeof(Device)))
replyDictionary[device.ToString()] = sendDictionary[device.ToString()].Send(Configs.instrumentSpecification[device.ToString()].ip).Status;
return true;
}
回答1:
My career was in Test and Measurement and doing this kind of thing seems quite familiar. So I'm offering this as one approach to see if I can assist you in pinging your instrument stack.
Getting a blue screen is pretty rare and just from personal experience it's likely that a very low-level IO kernel driver (ethernet port?) is having a bad time of something. Potentially, it appears that the loop could run very fast to the point of overrunning a buffer perhaps? Or, pathologically, you may inadvertently be binding the UI thread to that of a kernel driver one. Ouch.
I agree with comments about safely calling the UI thread from a task using MethodInvoker. But I think perhaps the main issue might be coming from the fact that this loop "runs as fast as it can" which might be pretty fast. I modeled something like this that throttles the pings so that they occur some limited number of times per second and it seems to work fine.
A safely-threaded task that performs the look without ever blocking on the UI thread looks something like this:
CancellationTokenSource _cts = null;
SemaphoreSlim ssBusy = new SemaphoreSlim(1);
private void ExecMulticastPing()
{
ssBusy.Wait();
Task.Run(() =>
{
try
{
_cts = new CancellationTokenSource();
do
{
List<Task<PingReply>> asyncPings = new List<Task<PingReply>>();
// Sends out the pings in rapid succession to execute asynchronously in parallel
foreach (var device in (Device[])Enum.GetValues(typeof(Device)))
{
asyncPings.Add(Task.Run(() => SinglePingAsync(device, _cts.Token)));
}
// Waits for all the async pings to complete.
Task.WaitAll(asyncPings.ToArray());
// See if flag is already cancelled
if (_cts.IsCancellationRequested) break;
foreach (var device in (Device[])Enum.GetValues(typeof(Device)))
{
SetPanelImage(device, asyncPings[(int)device].Result);
}
// I believe that it's very important to throttle this to
// a reasonable number of repeats per second.
Task.Delay(1000).Wait();
BeginInvoke((MethodInvoker)delegate
{
WriteLine(); // Newline
});
} while (!_cts.IsCancellationRequested); // Check if it's cancelled now
}
finally
{
ssBusy.Release();
}
BeginInvoke((MethodInvoker)delegate
{
WriteLine("CANCELLED");
});
});
}
... where ...
const string URL_FOR_TEST = @"www.ivsoftware.com";
private PingReply SinglePingAsync(Device device, CancellationToken token)
{
if(token.IsCancellationRequested)
{
return null;
}
Ping pingSender = new Ping();
PingOptions options = new PingOptions()
{
DontFragment = true
};
PingReply reply = pingSender.Send(URL_FOR_TEST);
BeginInvoke((MethodInvoker)delegate
{
if (reply.Status == IPStatus.Success)
{
WriteLine("Address: " + reply.Address.ToString());
WriteLine("RoundTrip time: " + reply.RoundtripTime);
WriteLine("Time to live: " + reply.Options.Ttl);
WriteLine("Don't fragment: " + reply.Options.DontFragment);
WriteLine("Buffer size: " + reply.Buffer.Length);
WriteLine();
}
else
{
WriteLine("REQUEST TIMEOUT");
}
});
return reply;
}
... and ...
private void SetPanelImage(Device device, PingReply reply)
{
BeginInvoke((MethodInvoker)delegate
{
WriteLine("Setting panel image for " + device.ToString() + " " + reply.Status.ToString() );
Panel selectedPanel = (
from Control unk in Controls
where
(unk is Panel) &&
(unk.Name == device.ToString()) // ... or however you go about finding the panel...
select unk as Panel
).FirstOrDefault();
if (selectedPanel != null)
{
switch (reply.Status)
{
case IPStatus.Success:
// Set image for enabled
break;
case IPStatus.TimedOut:
// Set image as disabled
break;
default:
// Set image as disabled
break;
}
}
});
}
This 10-second screen capture demonstrates the success of this approach and you can browse the complete example code on our GitHub repo if you think this would be helpful. I also answered a different-but-related question here.
来源:https://stackoverflow.com/questions/62259706/using-ping-in-infinite-loop