weeraa
weeraa

Reputation: 1195

Cancel parallel process

I have a MainProcess.cs and ChildProcess.cs. Mainprocess is used to gather all required info and submit all data to parallel foreach loop. Child process will call to seperate API.

MainProcess.cs

//MainProcess.cs

string resourceId = GenerateUID();
Parallel.ForEach(Accounts, parallelOptions, async (account) =>
{                
    await childProcess.ProcessMethod(resourceId, account);

});

ChildProcess.cs

//ChildProcess.cs

public async Task ProcessMethod(string resourceId, string account){

    var Req = {}; //Required info set in here.

    using (HttpClient client = new HttpClient())
    {

        var result = await client.PostAsJsonAsync($"{baseUrl}api/services/app/Process/Run", Req);
        if (result.IsSuccessStatusCode)
        {
            //Save returned data to DB against the resourceId. (Figure 1)
        }
    }
}

Data save a table like below.

Figure 1

enter image description here

There is MainProcess class and it has a method to run tasks parallelly. There is a ChildProcess class and it has "ProcessMethod". Inside "ProcessMethod" I call to separate API to do some work. Above mention code and procedure is working 100% correctly.

But my issue is, at a point I need to cancel processing. Lets use below "figure 2" to make this easier to understand.

Figure 2

enter image description here

According to above figure, total process will finish approximate in 10 seconds. At the 4th second, I need to cancel processing. (Call 1 and Call 5 already returned the response) Call 2, Call 3, Call 4 are sent the request, but still not received the response.

What I need to do is, cancel all the processes at a given time. Simply It's like, I typed a Url in the browser and hit enter. before appearing the web page I close the browser. So I don't care about the responce.

I try to do this with cancellation token. But where I stuck is, I couldn't cancel api calls for specific resourceId.

Then I modified the code of ChildProcess.cs.

added instance varibale

CancellationTokenSource tokenSource = new CancellationTokenSource();

then change

var result = await client.PostAsJsonAsync($"{baseUrl}api/services/app/Process/Run", Req, tokenSource.Token);

I know, it should have to use below code for cancel process.

tokenSource.Cancel();

But the problem is how to cancel specific resourceId's process from a different function? (Cancel specific resourceId's process, because ProcessMethod can be called concurrently. If it is processing 2nd resourceId's process, It should not effect to 2nd process. 1st resourceId batch should have to close/cancel)

Any expert can help me on this?

Upvotes: 0

Views: 153

Answers (1)

Stephen Cleary
Stephen Cleary

Reputation: 457422

Above mention code and procedure is working 100% correctly.

It's really not. async cannot be used with Parallel. Instead of parallelism (multiple threads), you should use asynchronous concurrency (multiple operations without a thread for each):

//MainProcess.cs
string resourceId = GenerateUID();
var tasks = Accounts.Select(account => childProcess.ProcessMethod(resourceId, account)).ToList();
await Task.WhenAll(tasks);

But the problem is how to cancel specific resourceId's process from a different function? (Cancel specific resourceId's process, because ProcessMethod can be called concurrently. If it is processing 2nd resourceId's process, It should not effect to 2nd process. 1st resourceId batch should have to close/cancel)

You don't need to do any special logic around this. You can use the same cancellation token for all of them because each operation will ignore cancellation requests after it has completed.

The only code you would need to add is to ignore any cancellation exceptions.

Something like this:

public async Task ProcessMethod(string resourceId, string account)
{
  try
  {
    var Req = {}; //Required info set in here.
    using (HttpClient client = new HttpClient())
    {
      var result = await client.PostAsJsonAsync($"{baseUrl}api/services/app/Process/Run", Req, tokenSource.Token);
      if (result.IsSuccessStatusCode)
      {
        //Save returned data to DB against the resourceId. (Figure 1)
      }
    }
  }
  catch (OperationCanceledException)
  {
  }
}

Upvotes: 3

Related Questions