I am currently in the process of retrofitting our long-running methods to be cancelable. I am planning on using System.Threading.Tasks.CancellationToken to implement that.
Our methods generally perform a few long-running steps (sending commands to and then waiting for hardware mostly), e.g.
void Run()
{
Step1();
Step2();
Step3();
}
My first (maybe stupid) thought on cancellation would transform this into
bool Run(CancellationToken cancellationToken)
{
Step1(cancellationToken);
if (cancellationToken.IsCancellationRequested)
return false;
Step2(cancellationToken);
if (cancellationToken.IsCancellationRequested)
return false;
Step3(cancellationToken);
if (cancellationToken.IsCancellationRequested)
return false;
return true;
}
which frankly looks horrible. This "pattern" would continue inside the single steps, too (and they are necessarily rather longish already). This would make Thread.Abort() look rather sexy, although I know its not recommended.
Is there a cleaner pattern to achieve this that does not hide away the application logic beneath lots of boilerplate code?
Edit
As an example for the nature of the steps, the Run
method could read
void Run()
{
GiantRobotor.MoveToBase();
Oven.ThrowBaguetteTowardsBase();
GiantRobotor.CatchBaguette();
// ...
}
We are controlling different hardware units that need to be synchronized to work together.
If the steps are somehow independend regarding the dataflow within the method, but can't be executed in a parallel matter, the following approach may be better readable:
void Run()
{
// list of actions, defines the order of execution
var actions = new List<Action<CancellationToken>>() {
ct => Step1(ct),
ct => Step2(ct),
ct => Step3(ct)
};
// execute actions and check for cancellation token
foreach(var action in actions)
{
action(cancellationToken);
if (cancellationToken.IsCancellationRequested)
return false;
}
return true;
}
If the steps don't need the cancellation token because you can split them up in tiny units, you can even write a smaller list definition:
var actions = new List<Action>() {
Step1, Step2, Step3
};
what about continuation?
var t = Task.Factory.StartNew(() => Step1(cancellationToken), cancellationToken)
.ContinueWith(task => Step2(cancellationToken), cancellationToken, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current)
.ContinueWith(task => Step3(cancellationToken), cancellationToken, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With