单击 WPF 按钮停止 Ping.SendAsync

Stop Ping.SendAsync by button click WPF

美好的一天。请帮助我如何在这里停止 SendPingAsync 方法,我尝试了 SendAsyncCancel 但它只停止了一个线程,我需要取消所有 pong 线程。

private void refreshbtn_Click(object sender, EventArgs e)
    {
        if (tokenSource != null) //check if its even initialized or not
            tokenSource.Cancel();

        lstNetworks.Items.Clear();
        string gate_ip = NetworkGateway();
        //Extracting and pinging all other ip's.
        string[] array = gate_ip.Split('.');
        for (int i = 1; i <= 255; i++)
        {
            string ping_var = array[0] + "." + array[1] + "." + array[2] + "." + i;

            //time in milliseconds           
            Ping(ping_var, 1, 4000);
        }
    }

    public void Ping(string host, int attempts, int timeout)
    {
        tokenSource = new CancellationTokenSource();
        Task.Factory.StartNew(() =>
        {
            try
                {
                    System.Net.NetworkInformation.Ping ping = new System.Net.NetworkInformation.Ping();
                    ping.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback);
                    ping.SendAsync(host, timeout, host);
                }
                catch
                {
                    // Do nothing and let it try again until the attempts are exausted.
                    // Exceptions are thrown for normal ping failurs like address lookup
                    // failed.  For this reason we are supressing errors.
                }
        }, tokenSource.Token);
    }

    private void PingCompletedCallback(object sender, PingCompletedEventArgs e)
    {
        // If an error occurred, display the exception to the user. 
        if (e.Reply.Status == IPStatus.Success)
        {

            string hostName = GetHostName(e.Reply.Address.ToString());
            string macAdress = GetMacAddress(e.Reply.Address.ToString());
            if (!Dispatcher.CheckAccess())
            {

                Dispatcher.Invoke(new Action(() =>
                {
                        lstNetworks.Items.Add(new InfoItem() { IP = e.Reply.Address.ToString(), MAC = macAdress, HOST = hostName });
                    lstNetworks.Items.SortDescriptions.Add(new SortDescription("IP", ListSortDirection.Ascending));
                }));
            }
        }
        else {
            //Console.WriteLine(String.Concat("Non-active IP: ", e.Reply.Address.ToString()))
        }
    }

每次我点击刷新按钮时,我都应该开始新的 ping 操作。在我的例子中,只停止了一个 ping 线程,但其余线程继续工作。

更新

我试过你写的,当我再次按下刷新按钮时,我的应用程序冻结,直到所有线程停止(30 秒)。但是当应用程序解冻时结果是一样的,我之前 SendAsync 的所有 ping 数据包都添加了我第二次发送的新 SensAsync 数据包。我不仅需要停止线程,还需要停止 SendAsync 线程。有一种方法 SendAsyncCancel,但我如何在取消令牌触发时同时调用它?

您每次都在创建 TokenSource 的不同实例。相反,只创建一个,将相同的实例传递给所有 Tasks。然后每个 Task 将检查 Token 是否有取消请求,然后您可以对每个 Task 进行 WaitAll,并将 Token 传递给它。

private async void refreshbtn_Click(object sender, EventArgs e)
{
    if (tokenSource != null) //check if its even initialized or not
        tokenSource.Cancel();

    lstNetworks.Items.Clear();
    string gate_ip = NetworkGateway();
    //Extracting and pinging all other ip's.

    tokenSource = new CancellationTokenSource();

    string[] array = gate_ip.Split('.');

    List<Task> tasks = new List<Task>();
    for (int i = 1; i <= 255; i++)
    {
        string ping_var = array[0] + "." + array[1] + "." + array[2] + "." + i;

        var task = Task.Factory.StartNew(() =>
        {
            if (tokenSource.Token.IsCancellationRequested) return;

            //time in milliseconds           
            Ping(ping_var, 1, 4000, tokenSource.Token);
        }, tokenSource.Token);
        tasks.Add(task);
    }
    await Task.WhenAll(tasks.ToArray());
}

public void Ping(string host, int attempts, int timeout, CancellationToken cancellationToken)
{
    try
    {
        System.Net.NetworkInformation.Ping ping = new System.Net.NetworkInformation.Ping();

        cancellationToken.Register(() => ping.SendAsyncCancel());

        ping.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback);
        ping.SendAsync(host, timeout, host);
    }
    catch
    {
        // Do nothing and let it try again until the attempts are exausted.
        // Exceptions are thrown for normal ping failurs like address lookup
        // failed.  For this reason we are supressing errors.
    }
}

更新:

添加了 async/await,因此它不会阻塞 UI。为每个 Ping 注册取消令牌,以便它可以自行取消。