top of page

Subscribe to get best practices, tutorials, and many other cool things directly to your email inbox

  • Writer's pictureAhmed Tarek

How to Cancel a Running Process in a Separate Request/Command in .NET C#

Learn how to cancel an already running process in a separate request in .NET C#


Learn how to cancel an already running process in a separate request/command in DotNet (.NET) CSharp (C#)
Photo by Markus Winkler on Unsplash

One of my friends was working on a unique project where he needed to be able to cancel a long running process but on a separate request. Weird? maybe but it happened. Let me tell you more…


He was working on a Web Application with heavy 3D modelling. This application was supposed to be used for building 3D models for a certain product and the end user at some point should have been able to download this model in a certain format.


However, the modeling process itself is so heavy and requires a lot of processing. That’s why the end user was supposed to provide some input, trigger the modelling process and at some point he was supposed to get an email when the model is ready for download.


Up till now, it’s ok. But, if the end user decides to apply some changes on the inputs, he can start a new modelling process, but what about the already running one? Is it wise to just keep it running on the server???


 

Learn how to cancel an already running process in a separate request/command in DotNet (.NET) CSharp (C#)
Photo by Jukan Tateisi on Unsplash

Step By Step


Ok, what I am going to explain here is not rocket science. However, let’s take it a step by step.

First, let me point out the following:

  1. Some best practices would be ignored/dropped in order to drive the main focus to the other concepts this article is about.

  2. We will not create a whole Web Application as this would include a lot of distractions. We would just use Console and Windows Forms apps.

  3. The code included into this article is for demonstration and explanation only. Before going to your production code, you need to apply some refactoring and cleaning.

  4. There are some challenges raised by the technique I am going to explain in this article. I would tell you more about these challenges at the right moment.


 

When You Don’t Have Control


Let’s say that we have a simple Console Application. We have a TooLongProcess method which is supposed to take some time for full execution.



Here we simulated this by looping 10 times and each iteration we wait for one second.


Then, calling this TooLongProcess method should be as follows:



Running this, would end up with the following result:


Learn how to cancel an already running process in a separate request/command in DotNet (.NET) CSharp (C#)

Great, but now, let me ask you, do you have any control on the already running TooLongProcess?


The answer would be simply No. Once the code inside TooLongProcess method starts, we don’t have any kind of control over it.


 

Let’s Have Some Control


Now, we want to do some changes so that we gain control over the already running TooLongProcess.


To achieve this, we would apply some changes on the TooLongProcess method itself as follows:



Now, as you can see, the TooLongProcess method is now expecting a passed in CancellationToken parameter. And, inside the loop, we are checking if the passed in cancellation token is cancelled. If yes, we just break out of the loop and log a message that the process is cancelled.


Then, calling this TooLongProcess method should be as follows:



Now, we have to create a cancellation token through a CancellationTokenSource and pass it to the TooLongProcess method. Then we wait for 5 seconds and then cancel the token.


Running this, would end up with the following result:


Learn how to cancel an already running process in a separate request/command in DotNet (.NET) CSharp (C#)

Great, so now we are able to cancel the already running TooLongProcess. However, we are still doing this in the same context/scope of firing the TooLongProcess in the first place.


What if I want to cancel the already running TooLongProcess from another scope?


What if the TooLongProcess resides in a Web API project and the decision of cancelling the process is taken by the end user at another time?


May be through a totally separate request?


 

The New Technique


To explain this technique, we are not going to create a whole Web API or Web Application project as this would create more distractions.


Instead, we will create a simple Windows Forms application. The application should look like the following:


Learn how to cancel an already running process in a separate request/command in DotNet (.NET) CSharp (C#)

A simple form, with 2 ListBox controls and 4 Button controls.


The main idea is that when we click on the “Start 1” button, a long running process would start and it would keep logging into the first list box. Whenever we click on the “Stop 1” button, the already running process would be cancelled.


This is the way we are simulating two totally separate requests as if these requests are triggered from the UI of a Web Application and the running process is actually a Web API hosted on a different server.


The same applies on “Start 2” and “Stop 2” buttons.


When running the application, you should see something like this:


Learn how to cancel an already running process in a separate request/command in DotNet (.NET) CSharp (C#)

What happened here is that we clicked on “Start 1” and “Start 2” almost at the same moment, then after 5 seconds we clicked on “Stop 1”. Therefore, process 1 is stopped earlier while process 2 has ended successfully after 10 seconds.


To be able to follow up with the rest of the article, you can access the code on this repository.


 

The Code


Now, were going to see how to make this happen. For simplicity, all the code would be included into the Form code as you would see. However, in the real world, this is not the good practice.



First, let me explain the main idea.


As we agreed before, when we click the “Start 1” button, this should start process 1. Here we are going to pass in a CancellationToken as we learned so that we gain control over the process.


However, we still need to do something to have the control even outside the context/scope of “Start 1” logic.


To do so, it is logical to keep a reference to something that enables us to cancel the passed in token even from another scope.


Additionally, we need to have something common between “Start 1” and “Stop 1” so that “Stop 1” is aware of how to access that reference we kept for “Start 1”.


Also, we need to keep in mind that when working with a Web Application, the reference we keep should be something that we can serialize and pass back and forth through HTTP requests.


Therefore, although the CancellationToken (or the CancellationTokenSource) itself is the reference we need, we can’t depend on it as something to send back and forth through HTTP requests.


Having that said, the best thing to do is to create some Id at the moment of creating the cancellation token, and then this Id should act as a key to be sent back and forth through HTTP requests. And, knowing that Id, should provide us with access to the actual CancellationToken (or the CancellationTokenSource).


Therefore, now, let’s go through the code and see what we can notice:

  1. We defined private Guid m_Process1 and private Guid m_Process2 to keep the Guid to be used for process 1 and process 2.

  2. These are static now but in the real world, these Guid values should be created on the fly and sent back to the API caller so that they can use later for cancellation if needed.

  3. We also defined private Dictionary<Guid, CancellationTokenSource> m_TokensCatalog to act as the catalog where we would keep each Guid and its related CancellationTokenSource.

  4. In the real world, this is something that should be saved and maintained on the Server. I have a note on this that I would tell you about at the end of this article. So, make sure you would not miss it.

  5. On the BtnStart1_Click event handler, we are creating the CancellationTokenSource, adding a record in the catalog, and calling the TooLongProcess method passing in the created cancellation token.

  6. The same logic applies on the BtnStart2_Click event handler.

  7. On the BtnStop1_Click event handler, we are using the Id and catalog to get access to the appropriate CancellationTokenSource and then fire the Cancel method on it.

  8. The same logic applies on the BtnStop2_Click event handler.


Simple, right?


 

The Challenge


You might have noticed something that you didn’t like.


As we explained on the previous section, on point #4 specifically, the catalog where we keep references of the CancellationTokenSource so that we can later use them for cancellation, is saved on the Server.


This is not good. Why? because now our APIs are not stateless, they are stateful.


In other words, the optimal thing to achieve is that every request should not own any knowledge about other previous or successive requests. This way, we can apply more enhancements and concepts like using load balancers.


However, now after having a catalog of references saved on a Server, this is not valid anymore.


For example, if “Start 1" request is directed to “Server 1”, for the cancellation to work, “Stop 1” request should also be directed to “Server 1”, otherwise, it would not work.


My question now to you, do you think it is not logical for things to be this way?


Ok, let me explain. In the first place, what you are aiming at is that we have the capability of cancelling an already running process, on a certain Server. Therefore, it is so logical that there should be some coupling with a certain Server.


However, what we don’t like is that we think we only have two options:

  1. Lose hope and drop the whole cancelling running process thing.

  2. Make the whole application Stateful.


But, I think this is not true. I think you can still keep your application Stateless and be able to cancel a running process. However, to do this, you will need more work on your load balancer.


Your load balancer should be responsible for keeping and maintaining a catalog mapping the request Id to the Server Id. Then, when a cancellation request is coming in with a certain request Id, the load balancer is aware of which Server to direct the cancellation request to. Finally, once the certain Server gets the cancellation request, the rest would be the same as before.


 

Learn how to cancel an already running process in a separate request/command in DotNet (.NET) CSharp (C#)
Photo by Jingda Chen on Unsplash

Final Words


Ok, now you know what we were talking about. However, I don’t know what you think.

I’d like so much to get your feedback, if you have faced the same requirements before? What have you done about it? What you liked? What you didn’t like?…


That’s it, hope you found reading this article as interesting as I found writing it.



Recent Posts

See All

Subscribe to get best practices, tutorials, and many other cool things directly to your email inbox

bottom of page