循环完成前退出Dowork的背景工人

发布于 2025-02-13 08:00:22 字数 4342 浏览 0 评论 0原文

我有一个发送ping对象的程序,它会发送ping请求,每个ping对象重复(有8个),等待1000ms,然后重复此操作,但用户指定了很多秒。

当告诉程序以2-3秒的时间运行时间较低时,循环按预期工作。成功输出成功的ping的量(如果它运行了2秒,那么我希望每个ping对象都有2个成功的ping),

这是我收到的意外行为的示例,就是我将秒数设置为运行为10,因此它应该循环遍历8个ping对象中的每个对象,请等待一秒钟,然后重复此for count> 10

我正在调试,循环按预期工作,但随后在6秒后突然退出。我可以看到count = 10,而i是6,但是工人退出和runworkerCompleted方法被调用。

我可以想到线程的问题(?),但我不确定。

这是背景工人dowork方法:

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

这是我开始使用一个按钮的方法:

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

这是等待1000毫秒的方法,

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

它循环的次数似乎是随机的,我只是对其进行了测试删除线后,以防.doevents();线正在干扰背景工作者。

while (timer1.Enabled)
{
    Application.DoEvents();
}

count = 10像往常一样运行它,这是两次,并且第一次运行一次,第二次跑了7次。

任何帮助都将不胜感激

答案 删除了背景工人并实施了async/等待

  • Charlieface提供的答案

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦情居士 2025-02-20 08:00:22

您不能混合async voidbackgroundworker.dowork,因为一旦您击中第一个等待dowork 处理程序已向工人出现。

我建议您完全删除backgroundworker,只需使用等待即可。

示例代码似乎缺少信息:什么是pings count文件?我试图猜测它是如何工作的,但是看来您正在为这些使用某种缓存字段,至少在file的情况下)可能是不可接受的。

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

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());
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文