Q: threads, tasks, states, need help picking the right one I need some help in choosing the right tool. I'm replacing the hardware controller that controls some pumps with a raspberry pi and writing code for it in c# .netcore. The pumps should run in a specific sequence and for a specified duration. With all the possible ways to accomplish this, I'm looking for the cleanest and interesting one.
The pumps should do the following:


*

*Turn on pump 1

*wait 15 seconds

*turn on pump 2

*wait 10 minutes

*turn on pump 3

*let pump 3 run for 20 minutes

*turn off pump 3

*wait 10 minutes

*turn off pump 2

*wait 15 seconds

*turn off pump 1


I looked into timers, threads, tasks, state machine but I have a hard time picking the right tool for this job. At all times, I also need to be able to stop immediately all pumps.
Thanks for your help.

A: I'd probably go with tasks.
public async Task Execute()
{
    await TurnOnPump1();
    await Task.Delay(TimeSpan.FromSeconds(15));
    await TurnOnPump2();
    await Task.Delay(TimeSpan.FromMinutes(10));
    await TurnOnPump3();
    //And so on..
}


A: To expand on the great answer from Magnus, here's how you could implement cancellation so you could stop executing the method (stop starting new pumps) if you decide to stop all of them.  
I posted this answer because OP specifically said that they need to be able to stop the pumps at all times, so Magnus' answer wouldn't quite work in certain scenarios.  

At all times, I also need to be able to stop immediately all pumps.

public async Task StartAll(CancellationToken ct)
{
    await TurnOnPump1(); // no ct here because these methods should take little to no time to execute
    await Task.Delay(TimeSpan.FromSeconds(15), ct);
    await TurnOnPump2();
    await Task.Delay(TimeSpan.FromMinutes(10), ct);
    await TurnOnPump3();
    //And so on..
}

public asnyc Task StopAll()
{
    // Your_CancellationTokenSource should be defined somewhere else
    Your_CancellationTokenSource.Cancel(); // this line makes Task.Delay throw a TaskCanceledException
    await StopPump1();  
    await StopPump2();   
    await StopPump3();   
    // ..
}

public async Task HowToCallStart()
{
    try
    {
        // Your_CancellationTokenSource should be defined somewhere else
        await StartAll(Your_CancellationTokenSource.Token);
    }
    catch (TaskCanceledException)
    {
        // Starting was canceled
    }
}

This way, StopAll can be called anytime during the starting and you don't get any issues.  
A few things to mention:  


*

*Your_CancellationTokenSource should of course be some variable outside of these methods so it can be shared. It needs to be of type CancellationTokenSource. 

*As you can see by the comment (both in code and below answer), I assumed that starting a pump would be very fast and take very little to no time. That is the reason I did not pass in my CancellationToken.
If turning on the pumps takes some time, consider using CancellationToken inside the TurnOnPumpX methods as well to abort if the operation was canceled. If you do so, you can simply pass in ct to those methods as well.

*You should add some code in the catch for when the operation is canceled. At least print out a debug message if the end-user doesn't need to see it.

