1

I have an MS chart control. I create a separate thread to populate it with points and after plotting each point, I put the thread to sleep for some time then plot the next point, so that the graph looks like its moving. Here is the code.

Task[] t = new Task[1];
t[0] = Task.Factory.StartNew(() => plotChartPoints());



   public void plotPoint(int x, double y, int series)
    {
        comparisonChart.Series[series].Points.AddXY(x, y);
    }

  public void refreshChart()
    {
        this.mainSplitContainer.Panel2.Refresh();
    }

    public void plotChartPoints()
    {
        //comparisonChart.Series[0].Points.DataBindXY(xValuesSeries1.ToArray(), yValuesSeries1.ToArray());
        //comparisonChart.Series[1].Points.DataBindXY(xValuesSeries2.ToArray(), yValuesSeries2.ToArray());
        for (int index = 0; index < xValuesSeries1.Count; index++)
        {
            if (comparisonChart.InvokeRequired)
            {
                comparisonChart.Invoke(new MethodInvoker(() => plotPoint(xValuesSeries1.ElementAt(index), yValuesSeries1.ElementAt(index), 0)));
                comparisonChart.Invoke(new MethodInvoker(() => plotPoint(xValuesSeries2.ElementAt(index), yValuesSeries2.ElementAt(index), 1)));
            }
            Thread.Sleep(50);
            if (this.mainSplitContainer.InvokeRequired)
            { 
                mainSplitContainer.Invoke(new MethodInvoker(()=> refreshChart()));
            }

        }
    }

Now, I want to add a button so that when the button is clicked the task which is populating the chart pauses and the chart freezes. How do I accomplish this? I am using .NET 4.0 and I don't see any method to pause a Task in the Task class

svick
  • 236,525
  • 50
  • 385
  • 514
Kaushik
  • 429
  • 1
  • 7
  • 19
  • Just a small query. You state 'I create a separate thread to populate it' however, I'm pretty sure that a new `Task` isn't always guaranteed to create a new thread. – MattC Feb 06 '14 at 11:50
  • Can you use VS2012+ and [`Microsoft.Bcl.Async`](http://www.nuget.org/packages/microsoft.bcl.async)? – noseratio Feb 06 '14 at 22:25

4 Answers4

2

While you could pause the Thread the Task is running on that would be even worse than the Sleep() you are already using.

You should replace all of this with a WinForms Timer. That eliminates the need for Invoke() and Sleep() and a Timer can be easily stopped (Enabled = false;).

H H
  • 263,252
  • 30
  • 330
  • 514
  • The best answer so far, +1. The OP is using a background thread only to callback the UI thread, apparently to be able to use `for` loop. A popular anti-pattern, here's a [similar case](http://stackoverflow.com/q/21592036/1768303). – noseratio Feb 06 '14 at 22:30
0

There is no built-in function to do this in Task or thread only OS scheduler can pause and resume thread once started. But a work-around may be to use the CancellationToken as an argument

TaskFactory.StartNew<TResult> Method (Func<TResult>, CancellationToken)

Once the button was clicked and store the current value of index in out of method scope variable and when you click resume you can start your index from the reached point

following a simple example how you can do it

 /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private ChartController _chartController = new ChartController();
        public bool paused; 

        public MainWindow()
        {
            InitializeComponent();

        }

        private void PlayPauseButton_OnClick(object sender, RoutedEventArgs e)
        {
            paused = !paused;
            if (!paused)
            {
                _chartController.CancelAnim();
            }
            else
            _chartController.StartTask();
        }
    }

    public class ChartController

    {
        private CancellationTokenSource tokenSource = new CancellationTokenSource();
        private CancellationToken _cancellationToken;

        private int reachedChartIndex = 0;
        public void CancelAnim()
        {
            tokenSource.Cancel();
        }
        public ChartController()
        {

        }

        public void StartTask()
        {
            Task t = Task.Factory.StartNew(() => plotChartPoints(), tokenSource.Token);
            _cancellationToken = tokenSource.Token;


            //to handle exeption if there is 
            try
            {
                t.Wait();
            }
            catch (AggregateException e)
            {
                foreach (var v in e.InnerExceptions) 
                //here manage task exception 
            }
        }



        public void plotPoint(int x, double y, int series)
        {
            comparisonChart.Series[series].Points.AddXY(x, y);
        }

        public void refreshChart()
        {
            this.mainSplitContainer.Panel2.Refresh();
        }

        public void plotChartPoints()
        {

            _cancellationToken.ThrowIfCancellationRequested();

            //comparisonChart.Series[0].Points.DataBindXY(xValuesSeries1.ToArray(), yValuesSeries1.ToArray());
            //comparisonChart.Series[1].Points.DataBindXY(xValuesSeries2.ToArray(), yValuesSeries2.ToArray());
            for (int index = reachedChartIndex; index < xValuesSeries1.Count; index++)
            {
                if (_cancellationToken.IsCancellationRequested)
                {
                    reachedChartIndex = index;
                    break;
                }
                if (comparisonChart.InvokeRequired)
                {
                    comparisonChart.Invoke(
                        new MethodInvoker(
                            () => plotPoint(xValuesSeries1.ElementAt(index), yValuesSeries1.ElementAt(index), 0)));
                    comparisonChart.Invoke(
                        new MethodInvoker(
                            () => plotPoint(xValuesSeries2.ElementAt(index), yValuesSeries2.ElementAt(index), 1)));
                }
                Thread.Sleep(50);
                if (this.mainSplitContainer.InvokeRequired)
                {
                    mainSplitContainer.Invoke(new MethodInvoker(() => refreshChart()));
                }

            }
        }

    }

}
BRAHIM Kamel
  • 13,492
  • 1
  • 36
  • 47
0

Could you possibly use an AutoResetEvent MSDN

Then on your button click you could signal the plotting to pause. I'm assuming that you will have an 'unpause' action when the button is clicked again.

For more control look into the ManualResetEventSlim MSDN

MattC
  • 3,984
  • 1
  • 33
  • 49
  • no it's used for synchronizing 2 threads and not suitable for start resume " AutoResetEvent allows threads to communicate with each other by signaling. Typically, you use this class when threads need exclusive access to a resource." – BRAHIM Kamel Feb 06 '14 at 11:42
  • @K.B Yeah, my thinking was that the button event and the chart population would be handled on different threads and so you could use the main UI thread to control operation of whatever background thread the Task was executing on. But it's probably a little bit 'round peg for square hole' usage for those classes. – MattC Feb 06 '14 at 11:46
0

One workaround would be not pausing the thread but rather using Thread.Sleep based on a variable (or property) which is set and reset by the UI thread.

    ...

        Thread.Sleep(50);//the sleep that you have in your code
        while (paused)
        {
        Thread.Sleep(100);
        }
    ...
cellik
  • 2,116
  • 2
  • 19
  • 29