异步线程没有停止

时间:2015-01-20 01:31:55

标签: c# asynchronous ping

我正在使用这个程序来同时ping大量的IP地址我尝试一次只ping一个地址但是一旦我开始ping 50多个主机它就变得非常长。我遇到的问题是,我无法在取消按钮单击时停止异步线程,并在尝试ping多个主机时出现此错误。我花了两天的时间试图搞清楚它并没有运气。确切的错误如下:

System.InvalidOperationException: An asynchronous call is already in
progress. It must be completed or canceled before you can call this method.
at System.Net.NetworkInformation.Ping.CheckStart(Boolean async)
at System.Net.NetworkInformation.Ping.Send(IPAddress address, Int32 timeout, Byte[] buffer, PingOptions options)
at 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);
        }
    }
}
}

1 个答案:

答案 0 :(得分:0)

您的代码的直接问题(即异常原因)是您在循环中同时调用Ping.Send()Ping.SendAsync()。拨打Ping.SendAsync()后,对Send()SendAsync()的任何后续调用都是非法的,直到第一次SendAsync()操作完成为止。

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

缺少a good, complete code example(此代码示例未显示完整的上下文,包括您获取ping地址的位置),很难确切知道您的方案中出现了什么问题。但我可以提供一些关于你发布的代码的观察结果:

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