如何使用按钮停止 for 循环

本文关键字:for 循环 何使用 按钮 | 更新日期: 2023-09-27 18:34:55

我有一个循环,我想停止使用按钮。为更好地理解而编辑:

我确实意识到您无法在循环运行时停止按钮,因为只要当前 UI 正在运行,它就不起作用。我真正要求的是创建线程或使用 BGWorker 来阻止这种情况的最有效方法。我见过一些方法,但其中大多数是针对 Java 而不是 C#。

我想做的是:

private void start_Click(object sender, EventArgs e)
{
    for(int i = 0; i < nums; i++)
    {
        doSomething();
    }
}
private void stop_Click(object sender, EventArgs e)
{
    stops start_Click()
}

如何使用按钮停止 for 循环

你不能

那样做。对于初学者来说,for循环在UI线程上同步运行,这意味着您甚至无法单击"停止"按钮。

因此,您需要将 for 循环的操作移动到另一个线程上,这意味着您可能根本不会使用 for 循环。您需要考虑内部代码实际需要如何执行,然后根据您如何进行处理,您可以实现"停止"按钮。

一个非常简单的方法是:

new Thread(() =>
{
   int i = 0;
   while (!stop && i < num)
   {
      doSomething();
      i++;
   }
 }).Start();

并设置stop以停止处理循环。在更现实的方案中,您可以将要处理的函数排队,然后通过类似的方法停止出列。不幸的是,在不知道更多详细信息的情况下很难推荐设置。

任何基于您的代码的解决方案也会遇到当前doSomething()完成执行的问题(这可能需要一段时间(。同样,如果没有更多信息,很难说解决这个问题的最佳方法是什么。

为了保持您的 UI 响应能够取消正在运行的操作,您可以使用 backgrounworker。后台工作者在其他线程中执行工作,同时保持 UI 响应:

    private readonly BackgroundWorker _backgroundWorker;
    public Form1()
    {
        InitializeComponent();
        _backgroundWorker = new BackgroundWorker
        {
            WorkerSupportsCancellation = true
        };
        _backgroundWorker.DoWork += backgroundWorker_DoWork;
        _backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
        Disposed += Form1_Disposed;
    }
    private void Form1_Disposed(object sender, EventArgs e)
    {
        _backgroundWorker.Dispose();
    }
    private void StartLoop()
    {
        if ( !_backgroundWorker.IsBusy )
        {
            _backgroundWorker.RunWorkerAsync();
        }
    }
    private void StopLoop()
    {
        _backgroundWorker.CancelAsync();
    }
    private void backgroundWorker_DoWork( object sender , DoWorkEventArgs e )
    {
        var backgroundWorker = ( BackgroundWorker ) sender;
        for ( var i = 0; i < 100; i++ )
        {
            if ( backgroundWorker.CancellationPending )
            {
                e.Cancel = true;
                return;
            }
            // Do Work
        }
    }
    private void backgroundWorker_RunWorkerCompleted( object sender , RunWorkerCompletedEventArgs e )
    {
        if ( e.Cancelled )
        {
            // handle cancellation
        }
        if ( e.Error != null )
        {
            // handle error
        }
        // completed without cancellation or exception
    }

我直言,这里最好的方法可能是将您的工作转换为异步操作,然后在循环中使用 async/await 习语。 例如:

private bool _stopLoop;
private async void start_Click(object sender, EventArgs e)
{
    _stopLoop = false;
    for(int i = 0; i < nums && !_stopLoop; i++)
    {
        await Task.Run(() => doSomething());
    }
}
private void stop_Click(object sender, EventArgs e)
{
    _stopLoop = true;
}

这允许循环本身在管理 _stopLoop 变量的 UI 线程中执行,但实际上不会阻止 UI 线程(这会阻止单击"停止"按钮(。

遗憾的是,您没有提供有关doSomething()工作原理的详细信息。可能有一种很好的方法可以将整个方法转换为async方法,但是如果没有实际代码,我无法对此发表评论。

请注意,此方法只会在每个操作之间的某个点中断循环。如果您希望能够中断doSomthing()操作本身,则必须为此提供一种机制。一种可能的方法是使用 CancellationSourceCancellationToken ,这提供了一种表达取消语义的便捷方法。

尝试使用 async/await 方法。这很容易!

public partial class MyForm : Form
{
    public MyForm()
    {
        InitializeComponent();
    }
    private CancellationTokenSource _tokenSource;
    private async void start_Click(object sender, EventArgs e)
    {
        if (_tokenSource != null)
            return;
        _tokenSource = new CancellationTokenSource();
        var ct = _tokenSource.Token;
        await Task.Factory.StartNew(() =>
        {
            for (; ; )
            {
                if (ct.IsCancellationRequested)
                    break;
                doSomething();
            }
        }, ct);
        _tokenSource = null;
    }
    private int _labelCounter;
    private void doSomething()
    {
        // do something
        Invoke((Action)(() =>
        {
            myLabel.Text = (++_labelCounter).ToString();
        }));
    }
    private void stop_Click(object sender, EventArgs e)
    {
        if (_tokenSource == null)
            return;
        _tokenSource.Cancel();
    }
}

试试这个:

bool stop=false;
private void start_Click(object sender, EventArgs e)
{
for(int i = 0; i < nums&& !bool; i++)
{
doSomething();
}
}

并在点击事件中

设置

stop=true;