How can I stop a for loop using a button?

I have a loop that I would like to stop using a button. Edited for better understanding:

I understand that you cannot stop the button while the loop is running, as it will not run while the current UI is running. What I'm really asking is the most efficient way to create a thread or use BGWorker to stop this. I've seen some methods, but most of them are for Java, not C #.

What I would like to do:

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()
}

      

+3


source to share


5 answers


You cannot do this. For starters, the loop for

runs synchronously on the UI thread, which means you can't even hit the Stop button.

Hence, you need to move the operations of the for loop to a different thread, which means that you probably won't use the loop for

at all. You need to think about how you actually want to execute the code internally, and then based on how you do the processing, you can implement a Stop button.

The easiest way to do this is simply:



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

      

And set stop

to stop the processing cycle. In a more realistic scenario, you can enqueue the functions you want to process and then stop deleting using a similar method. Unfortunately, it's hard to recommend without knowing the details.

Any solution based on your code will also have a current execution issue doSomething()

(which may take a while). Again, without further information, it's hard to tell which is the best approach to fix.

+7


source


You can use the backgrounworker to keep your UI responsive when the current operation can be canceled. The phonoverter does the work on a different thread while keeping your 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
    }

      

+5


source


IMHO, most likely the best approach here is to convert your work to an asynchronous operation and then use the idiom async

/ await

for a loop. For example:.

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;
}

      

This allows the loop itself to execute on the UI thread where the variable is being manipulated _stopLoop

, but without actually blocking the UI thread (which, among other things, would prevent the Stop button from being pressed).

Unfortunately, you did not provide details on how it works doSomething()

. There might be a good way to convert this method to a method async

, but I cannot comment on this without actual code.

Note that this approach will only break the loop at a point between each operation. If you want to abort the operation doSomthing()

, you will have to provide a mechanism to do so. One possible approach would be to use CancellationSource

and CancellationToken

, which provides a convenient way to express cancellation semantics.

+2


source


Try using the async / await approach. It's pretty simple!

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();
    }
}

      

0


source


try this:

bool stop=false;

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

      

and in the click event

set

stop=true;

      

-1


source







All Articles