Home > front end >  Best way to not wait for a task to be completed
Best way to not wait for a task to be completed

Time:12-05

Let's say we have an entity Team with fields Id, Name, Points and CompetitionId.

Based on this entity, I have a list saved in memory, with aggregate data for each team.

When I add some results, some lines in the table Teams, I want also to update this list, but not to wait for its result.

public async Task AddResults(List<Team> teams) {
  await context.AddRange(teams);

  await inMemoryService.SetRanking();
}

Inside of SetRankings method I get the teams lines from context and build the aggregate data. But I don't want to wait for that to be finished because is a long process (take ~ 10 minutes and will be increased each time). For that, I tried two methods:

1: to not use await keyword:

_ = inMemoryService.SetRanking(); this works only because I'll not wait for the task to be completed. BUT, the new aggregated list from memory will be created on the another thread (I think), and when I'll try to get the data, I'll receive the old one.

2: using ConfigureAwait with false value:

await inMemoryService.SetRanking().ConfigureAwait(false) here, the request is still locked until this task is completed.

How can I solve this? thx

CodePudding user response:

The best way to not wait for a task to be completed is to give that task to another piece of code.

AddResults could add the work to a queue and return. Then, another piece of code processes that work.

The queue itself can be:

  • and in-memory queue,
  • a queue service that your chose cloud provider offers,
  • even a database table.

The 'another piece of code' which monitors and processes the queue could be:

CodePudding user response:

One solution could be to run the SetRanking method in a separate background thread. This way, the main thread will not have to wait for it to complete, and you can continue processing other tasks.

To do this, you can use the Task.Run method, which will run the specified method in a separate thread and return a Task object that you can use to monitor the progress of the operation. For example:

public async Task AddResults(List<Team> teams) {
  await context.AddRange(teams);

  // Start the SetRanking method in a separate background thread
  var task = Task.Run(() => inMemoryService.SetRanking());

  // You can continue processing other tasks here, while SetRanking runs in the background
  // ...

  // If you want to wait for SetRanking to complete, you can use the await keyword
  await task;
}

Another option could be to use the Task.Factory.StartNew method, which provides more options for configuring the background thread. For example, you can specify the TaskScheduler to use, or set the CancellationToken that can be used to cancel the operation.

public async Task AddResults(List<Team> teams) {
  await context.AddRange(teams);

  // Start the SetRanking method in a separate background thread
  var task = Task.Factory.StartNew(() => inMemoryService.SetRanking(), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);

  // You can continue processing other tasks here, while SetRanking runs in the background
  // ...

  // If you want to wait for SetRanking to complete, you can use the await keyword
  await task;
}

You can also use the Task.WhenAll method to run multiple tasks in parallel and wait for all of them to complete. This can be useful if you have multiple tasks that you want to run in parallel and wait for all of them to complete before continuing. For example:

public async Task AddResults(List<Team> teams) {
  await context.AddRange(teams);

  // Start multiple tasks in parallel
  var task1 = Task.Run(() => inMemoryService.SetRanking());
  var task2 = Task.Run(() => otherService.DoSomethingElse());
  var task3 = Task.Run(() => yetAnotherService.DoSomethingElse());

  // Wait for all tasks to complete
  await Task.WhenAll(task1, task2, task3);
}

One option is to use the Task.ContinueWith method to schedule a continuation for the SetRanking task. This continuation will be executed when the SetRanking task completes, and it can do any additional processing that you need. Here is an example:

public async Task AddResults(List<Team> teams) {
  await context.AddRange(teams);

  // Execute the SetRanking method in a separate thread
  var setRankingTask = Task.Run(() => inMemoryService.SetRanking());

  // Schedule a continuation that will be executed when the SetRanking task completes
  setRankingTask.ContinueWith(t => {
    // Perform any additional processing here
  });

  // Move on from the AddResults method without waiting for the SetRanking task to complete
}

In this example, the AddResults method will move on without waiting for the SetRanking task to complete. The continuation will be executed when the SetRanking task completes, allowing you to perform any additional processing that you need. However, keep in mind that the continuation will be executed in a separate thread, so you may need to handle any thread-safety issues in your code.

I hope this helps

  • Related