【问题标题】:Asynchronous threads not stopping异步线程不停止
【发布时间】:2015-01-20 01:31:55
【问题描述】:

我正在开发这个程序以同时 ping 大量 IP 地址,我尝试一次简单地 ping 每个地址,但是一旦我开始 ping 50 多个主机,它就会变得非常长。我遇到的问题是,当我尝试 ping 超过 1 个主机时,我无法在单击取消按钮时停止异步线程并收到此错误。我花了 2 天时间试图弄明白,但没有运气。具体错误如下:

System.InvalidOperationException:异步调用已经在 进步。必须先完成或取消,然后才能调用此方法。 在 System.Net.NetworkInformation.Ping.CheckStart(布尔异步) 在 System.Net.NetworkInformation.Ping.Send(IPAddress 地址,Int32 超时,字节 [] 缓冲区,PingOptions 选项) 在 MultiPing.Form1.backgroundWorker1_DoWork(Object sender, DoWorkEventArgs e) in f:\Dev\tfsMultiPing\Multi Ping\MultiPing\MultiPing\Form1.cs:line 139
    private void pingBtn_Click(object sender, EventArgs e)
    {
        try
        {
            if (inputBox.Text == "")
            {
                MessageBox.Show("Please Enter an IP Address to Ping.",     "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                if (backgroundWorker1.IsBusy != true)
                {
                    backgroundWorker1.RunWorkerAsync();
                }
                else
                {
                    MessageBox.Show("Please Cancel current Ping or wait     for it to be completed");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }



     public void backgroundWorker1_DoWork(object sender,     System.ComponentModel.DoWorkEventArgs e)
    {
        try
        {
            this.Invoke(new MethodInvoker(delegate {     progressBar1.Enabled = true; }));
            int i;

            //Add each line in the input box to the "allLines" string     array
            string[] allLines = inputBox.Lines;
            Ping pingSender = new Ping();

            try
            {
                //Get an object that will block the main thread
                AutoResetEvent waiter = new AutoResetEvent(false);

                //When the PingCompleted even is raised,
                //The PingCompletedCallback method is called.
                pingSender.PingCompleted += new     PingCompletedEventHandler(PingCompletedCallback);

                //Create a buffer of 32 bytes of data to be transmitted.
                string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
                byte[] buffer = Encoding.ASCII.GetBytes(data);

                //Wait 2 seconds for a reply.
                int timeout = 2000;

                //Set Options for transmission:
                //The data can go through 64 gateways or routers
                //before it is destroyed, and the data packet
                //cannot be fragmented
                PingOptions options = new PingOptions(64, true);

                //Check if Cancel Button was clicked
                if (backgroundWorker1.CancellationPending == true)
                {
                    e.Cancel = true;
                    return;
                }
                else
                {
                    //Begin Loop to ping each given IP Address
                    for (i = 0; i < allLines.Length; i++)
                    {
                        //Check if Cancel Button was clicked
                        if (backgroundWorker1.CancellationPending ==     true)
                        {
                            e.Cancel = true;
                            return;
                        }
                        else
                        {
                            //Convert each line from the input box to an     IP address
                            IPAddress address =     IPAddress.Parse(allLines[i]);

                            //Send ping Asynchronously
                            //Use the waiter as the user token.
                            //When the callback complets, it can wake up     this thread.
                            pingSender.SendAsync(address, timeout,     buffer, options, waiter);
                            PingReply reply = pingSender.Send(address,     timeout, buffer, options);
                            waiter.WaitOne();
                            //If a replay is recieved Print "IP Address"     is up in the output box.
                            if (reply.Status == IPStatus.Success)
                            {
                                this.Invoke(new MethodInvoker(delegate {     outputBoxLive.AppendText(address + " is up" + Environment.NewLine); }));
                            }

                            //If no reply is recieved  then print "IP Address" is down in the output box.
                            else if (reply.Status == IPStatus.TimedOut)
                            {
                                this.Invoke(new MethodInvoker(delegate     {     outputBoxDown.AppendText(address + " is down" + Environment.NewLine); }));
                                pingSender.Dispose();
                            }

                            pingSender.Dispose();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }

    public void pingStuff()
    {
    }

    private static void PingCompletedCallback(object sender,     PingCompletedEventArgs e)
    {
        //If the operation was cancelled, Display a message to the user
        if (e.Cancelled)
        {
            MessageBox.Show("Ping Cancelled");

            //Let the main thread resume.
            //User token is the AutoResetEvent object that the main     thread is waiting for
            ((AutoResetEvent)e.UserState).Set();
        }

        //If an error occurred, Display the exception to the user.
        if (e.Error != null)
        {
            MessageBox.Show("Ping Failed: " + e.Error.ToString());

            //Let the main thread resume.
            ((AutoResetEvent)e.UserState).Set();
        }
        PingReply reply = e.Reply;
        DisplayReply(reply);

        //Let the main thread resume.
        ((AutoResetEvent)e.UserState).Set();
    }

    public static void DisplayReply(PingReply reply)
    {
        if (reply == null)
            return;

        Console.WriteLine("ping status: [0]", reply.Status);
        if (reply.Status == IPStatus.Success)
        {
            Console.WriteLine("Address: {0}", reply.Address.ToString());
            Console.WriteLine("RoundTrip time: {0}",     reply.RoundtripTime);
            Console.WriteLine("Time to live: {0}", reply.Options.Ttl);
            Console.WriteLine("Don't fragment: {0}",     reply.Options.DontFragment);
            Console.WriteLine("Buffer size: {0}", reply.Buffer.Length);
        }
    }




   public void button2_Click(object sender, EventArgs e)
    {
        try
        {
            backgroundWorker1.CancelAsync();
        }
        catch (Exception exc)
        {
            Console.WriteLine(exc);
        }
    }
}
}

【问题讨论】:

  • 不阅读代码..您可能可以将取消令牌传递给异步方法。
  • 与论坛网站不同,我们不使用“谢谢”、“任何帮助表示赞赏”或Stack Overflow 上的签名。请参阅“Should 'Hi', 'thanks,' taglines, and salutations be removed from posts?.
  • 如果你调用Invoke,你需要从回调中调用EndInvoke,这会结束调用。
  • @Mgetz:您正在考虑关于BeginInvoke() 的规则,而不是Invoke()。它不适用于这里。请注意,虽然按照惯例应该匹配一对 BeginXXX() 和 EndXXX() 方法,但在 Control.BeginInvoke() 和编译器生成的委托类型的 BeginInvoke() 方法的情况下,Microsoft 多年来一直提供以下指导:在这些情况下,无需致电EndInvoke()(即它们是规则的例外)。

标签: c# asynchronous ping


【解决方案1】:

您的代码的直接问题(即异常的原因)是您在循环中同时调用了Ping.Send() 和Ping.SendAsync()。一旦您调用了Ping.SendAsync(),在第一个SendAsync() 操作完成之前,对Send() 或SendAsync() 的任何后续调用都是非法的。

目前尚不清楚为什么您同时拥有两者。鉴于您似乎想要同步进行调用,最简单的方法是简单地删除对SendAsync() 的调用以及与之相关的任何内容(即waiter 对象)。或者,由于您似乎希望能够中断操作,您可能更愿意删除对Send() 的调用,以便可以使用SendAsyncCancel() 方法来中断未完成的操作。

缺少a good, complete code example(此代码示例未显示完整的上下文,包括您从何处获取要 ping 的地址),很难确切知道您的方案中出了什么问题。但我可以对您发布的代码提出几点意见:

  1. 您在循环的每次迭代中都调用了Ping.Dispose() 方法。这意味着只有在循环的第一次迭代中,您才能期望成功。在下一次迭代中,您应该会收到 ObjectDisposedException,终止工作!
  2. 如果您想取消正在进行的对Ping.SendAsync() 的调用,您应该调用Ping.SendAsyncCancel() 方法。这将导致当前 ping 操作以取消状态完成。当然,要做到这一点,您需要以某种方式公开对表单代码的 pingSender 引用,以便 button2_Click() 方法可以访问它。

【讨论】:

  • 代码调用 Ping.SendAsync,然后在下一行调用 Ping.Send。这解释了 InvalidOperationException。有趣的是,读取 inputBox.Lines 的工作线程不会引发跨线程异常(它不会编组到 UI 线程);也许它最终会,或者在不同的机器上。
  • 啊,没注意到。谢谢格罗弗男孩。我会修改我的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-06
  • 1970-01-01
  • 1970-01-01
  • 2011-12-10
  • 2021-12-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多