1

I have two buttons start and stop. On start button's click, i would like to hit a table and loop thro' the records, display the difference of time(time taken) when records are in the loop.

However when the start button is clicked, since the number of records are high, i can't click "stop" button , I am trying to click start button, run that process independently, for example, if 5000 records are processed, when i click stop button, i would like to display the time taken for those 5000 records, again when start is clicked, continue the records where it left off from.

How can i handle two events independently ? Any piece of thoughts would be really helpful.Thanks in advance.

private void startQueries()
{


thread=

new Thread(this.LoadResults);

thread.IsBackground =true;

thread.Start();

}

private void LoadResults()
{
 //Method that runs a select query - fetches 1000s of records
 Timespan startTime = <<Record the start time>>;

 //Loop thro' all the 1000s of records;

Timespan stopTime=<<Record the stop time>>;

//Display the difference between start and stop time.

}

    private void btnStart_Click(object sender, EventArgs e)
    {

    if(thread!=null)
    {
    if (thread.ThreadState == ThreadState.Running)
    {
    //do nothing
    }
    else if (thread.ThreadState == ThreadState.Suspended)
    {
    startQueries();
    }

    }

private void btnStop_Click(object sender, EventArgs e)
{
  //Stop the running thread

 // Need to display the time between the thread start and stop.
}
}
Avi Turner
  • 10,234
  • 7
  • 48
  • 75
Sharpeye500
  • 8,775
  • 25
  • 95
  • 143
  • you surely have to use some kind of `thread` (even `Backgroundworker` or `timer` is just simple special kind of thread). – King King Jul 09 '13 at 04:16
  • Check this: http://stackoverflow.com/questions/3065700/how-to-start-and-stop-a-continuously-running-background-worker-using-a-button – Evgeny Bychkov Jul 09 '13 at 04:33

2 Answers2

1

You can use the benefits of a BackgroundWorker.

You can skip right to the code, but just for general information, the BackgroundWorker wraps up the management of the background thread in a friendly way. The main events you need to use are:

  1. DoWork- triggered on another thread (i.e. asynchronously), this is for your heavy, slow job. it is being triggered by calling the "RunWorkerAsync" method.
  2. ProgressChanged - it is being triggered on the thread where "RunWorkerAsync" was called, typically your GUI thread. it is being triggered by calling "workerInstance.ReportProgress()", usually from your actual working function.
  3. RunWorkerCompleted - triggered on your on the thread where "RunWorkerAsync" was called, typically your GUI thread. It is triggered when the DoWork event handler returns. This is for actions required after the job has completed.

The way I see it you have 2 options:

  1. If you can prematurely exit the LoadResults functions:

    /// <summary>
    /// The BackgroundWorker to handle you async work
    /// </summary>
    BackgroundWorker bw = new BackgroundWorker
    {
        WorkerReportsProgress = true,
        WorkerSupportsCancellation = true
    };
    
    /// <summary>
    /// Handles the Click event of the btnStart control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
    private void btnStart_Click(object sender, EventArgs e)
    {
        if (bw.IsBusy)
        {
            return;
        }
    
        System.Diagnostics.Stopwatch sWatch = new System.Diagnostics.Stopwatch();
        bw.DoWork += (bwSender, bwArg) =>
        {
            //what happens here must not touch the form
            //as it's in a different thread        
            sWatch.Start();
            this.LoadResults();
        };
    
        bw.ProgressChanged += (bwSender, bwArg) =>
        {
            //update progress bars here
        };
    
        bw.RunWorkerCompleted += (bwSender, bwArg) =>
        {
            //now you're back in the UI thread you can update the form
            //remember to dispose of bw now               
    
            sWatch.Stop();
            MessageBox.Show(String.Format("Thread ran for {0} milliseconds",sWatch.ElapsedMilliseconds));
            //work is done, no need for the stop button now...
            this.btnStop.Enabled = false;
            bw.Dispose();
        };
    
        //lets allow the user to click stop
        this.btnStop.Enabled = true;
        //Starts the actual work - triggerrs the "DoWork" event
        bw.RunWorkerAsync();
    
    
    }
    
    /// <summary>
    /// Handles the Click event of the btnStop control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
    private void btnStop_Click(object sender, EventArgs e)
    {
        //Stop the running thread
        this.bw.CancelAsync();
        // Need to display the time between the thread start and stop.
    }
    
    private void LoadResults()
    {
        //When you check if cancelation is pending:
        for (int i = 0; i < 5; i++)
        {
            //Simulating some job time
            Thread.Sleep(1000);
    
            if (bw.CancellationPending)
            {
                return;
            }
    
        }
    
    
    }
    
  2. If you cannot prematurely exit the LoadResults functions: (based on Robs answer)

        /// <summary>
        /// The BackgroundWorker to handle you async work
        /// </summary>
        BackgroundWorker bw = new BackgroundWorker
        {
            WorkerReportsProgress = true,
            WorkerSupportsCancellation = true
        };
    
        /// <summary>
        /// Handles the Click event of the btnStart control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            if (bw.IsBusy)
            {
                return;
            }
    
            System.Diagnostics.Stopwatch sWatch = new System.Diagnostics.Stopwatch();
            bw.DoWork += (bwSender, bwArg) =>
            {
                //what happens here must not touch the form
                //as it's in a different thread        
                sWatch.Start();
    
    
                var _child = new Thread(() =>
                {
                    this.LoadResults();
    
                });
                _child.Start();
                while (_child.IsAlive)
                {
                    if (bw.CancellationPending)
                    {
                        _child.Abort();
                        bwArg.Cancel = true;
                    }
                    Thread.SpinWait(1);
                }                
    
            };
    
            bw.ProgressChanged += (bwSender, bwArg) =>
            {
                //update progress bars here
            };
    
            bw.RunWorkerCompleted += (bwSender, bwArg) =>
            {
                //now you're back in the UI thread you can update the form
                //remember to dispose of bw now               
    
                sWatch.Stop();
                MessageBox.Show(String.Format("Thread ran for {0} milliseconds",sWatch.ElapsedMilliseconds));
                //work is done, no need for the stop button now...
                this.btnStop.Enabled = false;
                bw.Dispose();
            };
    
            //lets allow the user to click stop
            this.btnStop.Enabled = true;
            //Starts the actual work - triggerrs the "DoWork" event
            bw.RunWorkerAsync();
    
    
        }
    
        /// <summary>
        /// Handles the Click event of the btnStop control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        private void btnStop_Click(object sender, EventArgs e)
        {
            //Stop the running thread
            this.bw.CancelAsync();
            // Need to display the time between the thread start and stop.
        }
    
        private void LoadResults()
        {
            //Simulation job time...
            Thread.Sleep(5000);
        } 
    
Community
  • 1
  • 1
Avi Turner
  • 10,234
  • 7
  • 48
  • 75
  • P.S. When using backgroundWorker, it is always convenient for me to use @Keith [template](http://stackoverflow.com/questions/74880/delegating-a-task-in-and-getting-notified-when-it-completes-in-c/75743#75743) – Avi Turner Jul 09 '13 at 07:55
0

If your are using .NET 4.5 or greater, you can use the newer asynchronous functionality which gives your code a clean, sequential looking flow even though it is still happening asynchronously.

public partial class Form1 : Form
{
    CancellationTokenSource _cancellationSource;
    int _currentRecord;
    int _maxRecord;

    public Form1()
    {
        InitializeComponent();
        _currentRecord = 0;
        _maxRecord = 5000;
    }

    private async void btnStart_Click(object sender, EventArgs e)
    {
        await StartQueriesAsync();
    }

    private async Task StartQueriesAsync()
    {
        _cancellationSource = new CancellationTokenSource();
        var sw = new Stopwatch();

        try
        {
            // for Progress<>, include the code that outputs progress to your UI
            var progress = new Progress<int>(x => lblResults.Text = x.ToString());
            sw.Start();
            // kick off an async task to process your records
            await Task.Run(() => LoadResults(_cancellationSource.Token, progress));
        }
        catch (OperationCanceledException)
        {
            // stop button was clicked
        }

        sw.Stop();
        lblResults.Text = string.Format(
            "Elapsed milliseconds: {0}", sw.ElapsedMilliseconds);
    }

    private void LoadResults(CancellationToken ct, IProgress<int> progress)
    {
        while(_currentRecord < _maxRecord)
        {
            // watch for the Stop button getting pressed
            if (ct.IsCancellationRequested)
            {
                ct.ThrowIfCancellationRequested();
            }

            // optionally call this to display current progress
            progress.Report(_currentRecord);

            // simulate the work here
            Thread.Sleep(500);

            _currentRecord++;
        }
    }

    private void btnStop_Click(object sender, EventArgs e)
    {
        _cancellationSource.Cancel();
    }
}

When you click the Start button, a task is kicked off via Task.Run and passed a CancellationToken to watch for the Stop button as well as a Progress object that contains code to update your UI as your long running tasks operates.

A Stopwatch object is also created before the task to record how long the task runs.

Also note that the current record is maintained so that when you resume after you've stopped, it will pick up where it left off.

Brad Rem
  • 6,036
  • 2
  • 25
  • 50