1

There is data processing in my program that can take quite a while. Essentially it's a loop that reads data from file in small segments and applies it to a data model step by step. Simplified code looks like this:

public void LoadFromFile()
{
    while (this.playbackFile.HasMoreData)
    {
        this.ProcessData(this.playbackFile); // Process a single data segment
    }
}

To keep my WPF UI responsive I wrapped it into a Task like this:

public Task LoadFile()
{
    return Task.Run(() => this.LoadFromFile());
}

And then simply use it when a button is clicked to start:

await this.playbackController.LoadFile();

Now what I want to do is to add controls for this operation: pause/resume, step, stop. I also want an ability to control execution speed, for example, to slow down processing to simulate real-time data input by calculating timestamps difference between previous and next data segments and increasing the time it takes to process next segment according to that (essentially causing a delay with an interval that changes between each segment). When I think about it I want to implement something that could control my task like this:

WaitFor(x); // Delay for x ms to simulate speed decrease
WaitFor(-1); // Indefinite delay on pause; waits until resume is signaled

While researching my options I found recommendations to use AsyncManualResetEvent or PauseToken to control pause/resume, though I am not sure how I can integrate delay with that.

Instead of having one large task it would make sense for me to make each segment processing a separate task (actually, it would be preferred to pause between data segments processing, not in the middle of operation, and stepping would require it anyway) and then I could possibly control each one of them with something like Task.Delay, but I am not sure if it's a good idea and if it allows me to do pause/resume.

I would be grateful if someone could point me in the right direction with this.

Lirrik
  • 795
  • 11
  • 17
  • Possible duplicate of [Async Tasks Pause Resume of Loop](http://stackoverflow.com/questions/20185848/async-tasks-pause-resume-of-loop) – David Pine Apr 29 '16 at 11:15
  • The SO Q / A I posted above should be exactly what you need. – David Pine Apr 29 '16 at 11:15
  • Pausing/resuming can be done with [`PauseToken`](http://blogs.msdn.com/b/pfxteam/archive/2013/01/13/cooperatively-pausing-async-methods.aspx) (also [available on NuGet](http://dotnetapis.com/pkg/Nito.AsyncEx.Coordination/1.0-beta-1/net46/doc/Nito.AsyncEx.PauseTokenSource)). But if you want to slow down *time*, you're probably better off using [`IScheduler`](http://www.introtorx.com/content/v1.0.10621.0/15_SchedulingAndThreading.html) - either [`TestScheduler`](http://www.introtorx.com/content/v1.0.10621.0/16_TestingRx.html) or a custom implementation. – Stephen Cleary Apr 29 '16 at 12:02
  • @DavidPine as I mentioned, I do know about `PauseToken`, my question is mostly about how to properly use something like that and control running execution speed as well. – Lirrik May 02 '16 at 05:41
  • @StephenCleary `IScheduler` looks intriguing, thank you for suggestion, but it's probably overkill for my needs. – Lirrik May 02 '16 at 05:43

2 Answers2

1

One of your specifications is the ability to step through your process step by step. So I assume you have an ordered sequence of steps. By this I mean you have something like: "perform the first step" and "perform the next step", or in software terms: your sequence of steps is Enumerable.

If you don't have such a sequence, then You can't step.

If you look at your process as an IEnumerable < Step > and each Step has a function Perform, then you could use linq to perform the sequence of steps you want. Not all of them might be useful

  • Perform all steps starting at the beginning
  • Perform three steps starting at the beginning
  • Perform the next step
  • Perform the next five steps
  • Continue stepping until step == ...
  • Continue stepping until all steps are performed
  • Skip the following three steps

Another specification is that you want to control the time used by a step. In other words: if a step is performed in less time than the required time, wait until the required time is passed.

Of course you can't step in a shorter time than the step actually needs to perform.

Having these specifications, It is sufficient if all your steps implement the following interface:

public interface ISteppable
{
    void Step();
}

The following should do

public class Step
{
    private ISteppable SteppableObject {get; set;}
    public Step(ISteppable steppableObject)
    {
        this.SteppableObject = steppableObject;
    }


    // just do the step
    public void Step()
    {
        this.SteppableObject.Step();
    }

    // do the step, and control execution speed:
    public void Step(TimeSpan minExecutionTime)
    {
        var waitTask = Task.Delay(minExecutionTime);
        var stepTask = Task.Run( () => this.SteppableObject.Step());
        // wait until both the step and the wait task are ready:
        Task.WaitAll( new Task[] {waitTask, stepTask}
        // now yo know this lasted at least minExecutionTime
    }

It is a fairly small change if you want this using async-await.

  • Interface needs an async Task Step() function
  • Step class needs async functions that return Task and await the Step()
  • Instead of WaitAll use await Task.WhenAll

Example of usage:

public class MyProcess()
{
    private ISteppable Step0 = new ...
    private ISteppable Step1 = new ...
    private ISteppable Step2 = new ...
    private ISteppable Step3 = new ...

    private IEnumerable<Step> steps = 
    {
        new Step(step0),
        new Step(step1),
        new Step(step0), // yes, my process needs step0 again!
        new Step(step2),
     }

     public IEnumerable<Step> Steps {get{return this.steps;}
 }

 static void Main()
 {
     MyProcess process = new MyProcess();
     // 3 steps full speed:
     foreach (var step in process.Steps.Take(3))
     {
         process.Step();
     }
     // slow speed until step == ...
     foreach (var step in process.Steps.Skip(3).While(step => step != ...)
     {
         process.Step(TimeSpan.FromSeconds(3);
     }
 }

To keep things simple and focussed on the task execution I've created my Step class such that each step does not know the next step. Hence the step class is not Enumerable and the user of the Step class should keep track which steps are already performed.

An improvement would be to let the step class implement IEnumerable. Each step knows the next step. This means that the user doesn't have to keep track which steps are already performed and which not. How to make an enumerator is discussed elsewhere.

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
  • Yes, I do have an ordered sequence of operations, it's one-by-one segment processing. It's a nice idea, I haven't thought about it this way, thank you for this detailed explanation. The only downside is that it is too "step"-centric and I'd prefer to keep my existing model. – Lirrik May 02 '16 at 05:32
1

If I understand it correctly, what you want is to asynchronously wait for all of the following:

  • AsyncManualResetEvent.WaitAsync() for stepping
  • PauseToken.WaitWhilePausedAsync() for pausing
  • Task.Delay() for slowing down

Fortunately for you, there is a method just for that: Task.WhenAll().

AsyncManualResetEvent steppingEvent;
PauseToken pauseToken;
TimeSpan delay; // set to Zero for no delay

while (this.playbackFile.HasMoreData)
{
    var delayTask = Task.Delay(delay);

    this.ProcessData(this.playbackFile); // Process a single data segment

    await Task.WhenAll(
        steppingEvent.WaitAsync(), pauseToken.WaitWhilePausedAsync(), delayTask);
}
svick
  • 236,525
  • 50
  • 385
  • 514
  • This seems to be an elegant and straightforward solution, thank you. I was a bit short-sighted thinking about a one-in-all solution instead of using all the separate pieces of puzzle for their corresponding operations at once. – Lirrik May 02 '16 at 05:06