I have a program that sends a ping object, it sends out the ping request, repeats for each ping object (there's 8), waits 1000ms, and then repeats this for however many seconds the user specifies.
When telling the program to run for a low amount of time 2-3 seconds, the loop works as expected. Outputting the amount of successful pings successfully (if it ran for 2 seconds then I would expect each ping object to have 2 successful pings which they do)
An example of the unexpected behaviour I'm receiving is this, I set the amount of seconds to run as 10, so it should loop through each of the 8 ping objects, wait one second, and repeat this for count > 10
I am debugging and the loop works as expected but then suddenly exits after around 6 'seconds'. I can see that count = 10
, and i is 6
but then the worker exits and the RunWorkerCompleted
method is called.
I can think of maybe its a problem with threading(?) but I am unsure.
Here is the background worker DoWork
method:
private async void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
// Create background worker
BackgroundWorker worker = (BackgroundWorker)sender;
// Write to log file
await file.WriteAsync("Starting job...\n");
await file.WriteAsync("Requested amount of pings: " count "\n");
// Create date object for logs
DateTime localDate = DateTime.Now;
for (int i = 0; i < count; i )
{
// Create ping objects
System.Net.NetworkInformation.Ping pinger = new System.Net.NetworkInformation.Ping();
PingReply pingReply;
foreach (Ping pingObj in pings)
{
try
{
pingReply = pinger.Send(pingObj.IpAddress);
// Write log file
await file.WriteLineAsync(localDate.TimeOfDay " | Friendly Name " pingObj.FriendlyName " | Ping: " pingReply.Address " | Status " pingReply.Status " | Time: " pingReply.RoundtripTime);
if (pingReply.Status.ToString().Contains("Success"))
{
pingObj.SuccessfulPings = 1;
}
else // Unsuccessful ping has been sent
{
pingObj.FailedPings = 1;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
wait(1000);
}
}
catch (Exception a)
{
Debug.WriteLine(a.ToString());
}
}
Here's how I begin the worker, with a button:
private void Ping_Btn_Click(object sender, EventArgs e)
{
int counter = 1;
if (backgroundWorker1.IsBusy != true)
{
// For every ping object
foreach (Ping pinger in pings)
{
// Set the appropriate UI element properties
// Create initial control object
// Finds controls in which their name matches the string
Control? ctl = this.Controls.Find("greenBox" counter, true).FirstOrDefault() as Control;
if (ctl != null)
{
ctl.Visible = false;
}
ctl = this.Controls.Find("orangeBox" counter, true).FirstOrDefault() as Control;
if (ctl != null)
{
ctl.Visible = false;
}
ctl = this.Controls.Find("redBox" counter, true).FirstOrDefault() as Control;
if (ctl != null)
{
ctl.Visible = true;
}
ctl = this.Controls.Find("status_Lbl" counter, true).FirstOrDefault() as Control;
if (ctl != null)
{
ctl.Text = "Initiated...";
}
counter ;
}
// Take input from text box
count = Convert.ToInt32(pingSeconds_TxtBox.Text);
// Start operation
backgroundWorker1.RunWorkerAsync();
}
}
Here is the method for waiting 1000 ms
public void wait(int milliseconds)
{
var timer1 = new System.Windows.Forms.Timer();
if (milliseconds == 0 || milliseconds < 0) return;
timer1.Interval = milliseconds;
timer1.Enabled = true;
timer1.Start();
timer1.Tick = (s, e) =>
{
timer1.Enabled = false;
timer1.Stop();
};
while (timer1.Enabled)
{
Application.DoEvents();
}
}
The amount of times it loops through seems to be random, I tested it just there after removing the lines just in case the .DoEvents(); line is interfering with the background worker.
while (timer1.Enabled)
{
Application.DoEvents();
}
Ran it as usual with count = 10
did this twice and the first time it ran through once, and the second time it ran through 7 times.
Any help would be appreciated
Answer
Removed background worker and implemented async/await
instead
- Answer provided by Charlieface
CodePudding user response:
You can't mix async void
and BackgroundWorker.DoWork
, because as soon as you hit the first await
then the DoWork
handler appears to the worker that it has finished.
I would advise you to remove the BackgroundWorker
entirely, and just use await
.
There appears to be missing information from your sample code: what are pings
count
and file
? I have tried to guess how that works, but it looks like you are using some kind of cached field for these, which (at least in the case of file
) is probably inadvisable.
SemaphoreSlim _sem = new SemaphoreSlim(1, 1);
private void Ping_Btn_Click(object sender, EventArgs e)
{
if (!await _sem.WaitAsync(0)) // bail-out if already executing
return;
try
{
int counter = 1;
// For every ping object
foreach (Ping pinger in pings)
{
// Set the appropriate UI element properties
// Create initial control object
// Finds controls in which their name matches the string
(this.Controls.Find("greenBox" counter, true).FirstOrDefault() as Control)?.Visible = false;
(this.Controls.Find("orangeBox" counter, true).FirstOrDefault() as Control)?.Visible = false;
(this.Controls.Find("redBox" counter, true).FirstOrDefault() as Control)?.Visible = true;
(this.Controls.Find("status_Lbl" counter, true).FirstOrDefault() as Control)?.Text = "Initiated...";
counter ;
}
// Take input from text box
count = Convert.ToInt32(pingSeconds_TxtBox.Text);
// Start operation
await DoPing();
}
finally
{
// release lock
_sem.Release();
}
}
private async Task DoPing()
{
try
{
// Write to log file
await file.WriteAsync("Starting job...\n");
await file.WriteAsync("Requested amount of pings: " count "\n");
// Create date object for logs
DateTime localDate = DateTime.Now;
using (System.Net.NetworkInformation.Ping pinger = new System.Net.NetworkInformation.Ping())
{
for (int i = 0; i < count; i )
{
foreach (Ping pingObj in pings)
{
try
{
var pingReply = await pinger.SendAsync(pingObj.IpAddress);
// Write log file
await file.WriteLineAsync(localDate.TimeOfDay " | Friendly Name " pingObj.FriendlyName " | Ping: " pingReply.Address " | Status " pingReply.Status " | Time: " pingReply.RoundtripTime);
if (pingReply.Status == IPStatus.Success)
{
pingObj.SuccessfulPings = 1;
}
else // Unsuccessful ping has been sent
{
pingObj.FailedPings = 1;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
await Task.Delay(1000);
}
}
}
catch (Exception a)
{
Debug.WriteLine(a.ToString());
}
}