【问题标题】:Cancel a fuction/process取消函数/进程
【发布时间】:2021-01-25 05:20:58
【问题描述】:

我目前有一个 ping IP 列表的应用程序。 用户单击一个按钮,它会持续 ping 所有 IPS 20 秒。 我想要做的是单击一个按钮来停止它。

我创建了一个名为“Process”的函数,它会在单击按钮时调用此进程。

我试图创建一个 bool 值和 while 循环,当它为 false 并让停止按钮将 bool 值更改为 true,但它似乎并没有停止任何事情,事实上我什至无法关闭窗口,直到ping 已完成。

我的代码如下。

private bool _stopLoop;

    private void btnPlay_Click(object sender, EventArgs e)
    {
        Process();
    }

private void Process()
{
    while (_stopLoop == false)
    {
        for (int i = 0; i < 20; i++)
        {
            foreach (DataGridViewRow dataGridView1Row in dataGridView1.Rows)
            {
                var count = 20;
                progressBar1.Value = i * progressBar1.Maximum / count;
                Application.DoEvents();
                var url = dataGridView1Row.Cells[1].Value.ToString();
                int timeout = 500;
                Ping ping = new Ping();

                PingReply pingreply = ping.Send(url, timeout);
                PingReply result = null;
                IPStatus status;

                result = pingreply;
                status = result.Status;

                if (status != IPStatus.Success)
                {
                    dataGridView1Row.Cells[0].Style.BackColor = Color.Red;
                    dataGridView1Row.Cells[0].Value = "Offline";
                }
                else
                {
                    dataGridView1Row.Cells[0].Style.BackColor = Color.Green;
                    dataGridView1Row.Cells[0].Value = "Online";
                }



            }
        }
    }
}

private void btnStop_Click(object sender, EventArgs e)
{
  _stopLoop = true;
}

【问题讨论】:

  • 您需要将 ping 逻辑与用户界面分开。您可以查看 Threading 或更简单的 BackgroundWorker 组件
  • 您的循环没有给第二个按钮机会。要为时间跨度做事,请使用计时器!
  • async/await 的主要用例。
  • 这里有很多不好的做法,但最重要的是使用DoEvents 和在 UI 线程上使用高延迟同步操作。正如 Alejandro 所暗示的那样,您会做得更好,完全删除 DoEvents 从而避免重新进入,并将阻塞操作更改为异步操作。让编译器处理异步工作流的编排;避免这样的代码是我们首先发明异步工作流的原因。

标签: c# winforms


【解决方案1】:

我只针对为什么循环不会停止的直接问题。实现后台线程或任务或后台工作者是更好的解决方案,但不是问题:)

正如我的评论所说,你只打破了你的“无限”循环,这是最外层的循环。

运行在您的 IP 上的两个内部循环( foreach (DataGridViewRow dataGridView1Row in dataGridView1.Rows){ [...] } 以及您的“执行 20 次”循环 for (int i = 0; i &lt; 20; i++){ [...] } 如果您希望它停止,则需要中断。

这意味着您可以像这样更改它们:

while (_stopLoop == false)
{
    for (int i = 0; i < 20; i++)
    {   
        foreach (DataGridViewRow dataGridView1Row in dataGridView1.Rows)
        {
            if(_stopLoop)
                 return; // finish execution and jump out of all loops, or use break to jump into the for loop, but then you need to break out of it as well
             
            /* your ping logic */

        }
    }
}

【讨论】:

  • 这完全符合我的需要。谢谢!
  • 仍然认为当前您在 UI 线程中执行此操作,只是因为您使用 Application.DoEvents() 您可以与 UI 交互,否则它会冻结。考虑其中一种替代解决方案,例如后台工作者、线程或任务:-)
【解决方案2】:

如果您使用 async/await,您的 GUI 可以响应,并且您的请求可以同时并发:

private CancellationTokenSource pingCancellation;

private async void btnPlay_Click(object sender, EventArgs e)
{
    const int pingTimeout = 500;
    var pingDuration = TimeSpan.FromSeconds(20);

    btnPlay.Enabled = false;
    dataGridView1.ReadOnly = true;

    var entries =
        dataGridView1.Rows.Cast<DataGridViewRow>()
                          .Where(r => !r.IsNewRow)
                          .Select(row => new { Row = row, Hostname = row.Cells[1].Value.ToString(), Ping = new Ping() })
                          .ToList();


    pingCancellation = new CancellationTokenSource();
    pingCancellation.Token.Register(() => entries.ForEach(e => e.Ping.SendAsyncCancel()));

    btnStop.Enabled = true;

    pingCancellation.CancelAfter(pingDuration);
    var finishTime = DateTime.UtcNow.Add(pingDuration);
    try
    {
        while (!pingCancellation.IsCancellationRequested)
        {
            await Task.WhenAll(entries.Select(async e =>
            {
                var result = await e.Ping.SendPingAsync(e.Hostname, pingTimeout);

                if (result.Status != IPStatus.Success)
                {
                    e.Row.Cells[0].Style.BackColor = Color.Red;
                    e.Row.Cells[0].Value = "Offline";
                }
                else
                {
                    e.Row.Cells[0].Style.BackColor = Color.Green;
                    e.Row.Cells[0].Value = "Online";
                }
            }).Concat(new[] { Task.Delay(pingTimeout, pingCancellation.Token) /*Rate limiting*/ }));

            progressBar1.Value = (int)Math.Min(progressBar1.Maximum * (pingDuration - (finishTime - DateTime.UtcNow)).Ticks / pingDuration.Ticks, progressBar1.Maximum);
        }
    }
    catch (TaskCanceledException)
    {
    }

    btnStop.Enabled = false;
    pingCancellation.Dispose();
    pingCancellation = null;
    foreach (var entry in entries)
    {
        entry.Ping.Dispose();
    }
    progressBar1.Value = 0;
    dataGridView1.ReadOnly = false;
    btnPlay.Enabled = true;
}

private void btnStop_Click(object sender, EventArgs e)
{
    if (pingCancellation != null && !pingCancellation.IsCancellationRequested)
    {
        pingCancellation.Cancel();
    }
}

【讨论】:

    猜你喜欢
    • 2011-05-21
    • 2019-06-11
    • 2021-03-06
    • 1970-01-01
    • 1970-01-01
    • 2014-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多