Home > OS >  Background worker exiting DoWork before loop is completed
Background worker exiting DoWork before loop is completed

Time:07-07

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());
    }
}
  • Related