how to continue completed tasks? - c#

public int CalcGroup(CancellationTokenSource cts)
{
try
{
CancellationToken ct = cts.Token;
if (cts.IsCancellationRequested == true && TaskWorkStatus.IsContinue == false) return 0;
for (int i = 0; i < _paralellTaskCount; i++)
{
int counter = CheckCounter(message);
if (counter >= 0)
{
var myTask = new Task<long>(() => CalcSingle(_personnelIds[counter].Item1), ct, TaskCreationOptions.LongRunning);
if(myTask.IsCompleted)
myTask.ContinueWith(t => CalcSingle(_personnelIds[counter].Item1), ct);
else
myTask.Start();
}
}
}
catch (Exception)
{
return 0;
}
return 1;
}
In above code block I want to run tasks that are Canceled and Completed, but it doesn't work.
What is my mistake?

0) Creating a new Task via the constructor is a bad practice since running it will later incur extra overhead(you can google the why's and how's later). Use Task.Run or Task.Factory.StartNew, depending on your .NET version.. (I've recently learned that Task.Run is a better practice for .NET 4.5 and later).
1) Creating a task does not start it, so the current logic will always skip the first condition, start the task and no continuation will ever occur.
2) even if you start the task, there is no guarantee that the task will finish in time for the condition checking.. this is why Task.ContinueWith has an overload which accepts a TaskContinuationOptions enumeration.. you have OnlyOnRanToCompletion, OnlyOnFaulted, etc..
3) To sum it up, here is your code, after a little fine tuning
for (int i = 0; i < _paralellTaskCount; i++)
{
object message;
int counter = CheckCounter(message);
if (counter >= 0)
{
var task = Task.Run(() => CalcSingle(_personnelIds[counter].Item1));
var continuation = task.ContinueWith((antecedent) => CalcSingle(_personnelIds[counter].Item1),
TaskContinuationOptions.OnlyOnRanToCompletion);
}
}
(Note that the continuation receives the antecedent task, which is the task it continues..
4) You do not seem to check if an exception has occured in any task, in any of the many ways provided.. note that wrapping the statement in a try catch clause will not transfer the exception to your thread.. (You need to access the Result or Exception properties, invoke Wait() or use the await keyword in order to accomplish that.
5) Here is an async implementation which does the same, and checks for exceptions
for (int i = 0; i < _paralellTaskCount; i++)
{
try
{
object message;
int counter = CheckCounter(message);
if (counter >= 0)
{
long res1 = await Task.Run(() => CalcSingle(_personnelIds[counter].Item1));
long res2 = await Task.Run(() => CalcSingle(_personnelIds[counter].Item1));
}
}
catch (AggregateException e)
{
//TODO handle
}
}
6) Please do read some articles which regard TPL, as it seems that you missed a few very important notions.

Related

How to implement a token system for limiting concurrency of a processor/IO heavy multithreading Tasks in C#?

This is a follow up question from this question: How do you run a variable number of concurrent parametrizable infinite loop type of threads in C#?
Say I have a value taskLimit (assume 20), of no of simultaneous Tasks "MyTask" which are created in the RunAsync method below:
protected override async Task RunAsync(CancellationToken cancellationToken)
{
var tasks = new List<Task>();
try
{
for (int i = 0; i < taskLimit; i++)
{
tasks.Add(MyTask(cancellationToken, i);
}
await Task.WhenAll(tasks);
}
catch (Exception e)
{
//Exception Handling
}
}
public async Task MyTask(CancellationToken cancellationToken, int a)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
//long running code, if possible check for cancellation using the token
//Do something useful here. Very Processor and IO heavy. Takes 5-10 minutes to complete.
//SomeHeavyTask can only concurrently run upto a limit of say 5. Implement a token system of sorts
while(freeTokens<1)
{
await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
}
freeTokens = freeTokens-1;
SomeHeavyTask(cancellationToken);
freeTokens = freeTokens+1;
//sleep for an independently parameterizable period, then wake up and repeat
await Task.Delay(TimeSpan.FromHours(parametrizableTaskDelay[i]), cancellationToken);
}
catch (Exception e)
{
//Exception Handling
}
}
}
Is it possible to do such a thing? Is there a better more formal approach supported natively in C# to achieve the same thing? Please note the essence of this question is that there are substantially less freeTokens than the taskLimit. And that each MyTask spends only 10% time in SomeHeavyTask(), and most of the time is spent in await Task.Delay().
You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive and add using System.Reactive.Linq; - then you can do this:
int taskLimit = 500;
int maxConcurrent = 5;
IObservable<Unit> query =
Observable
.Range(0, taskLimit)
.Select(x => Observable.FromAsync(ct => SomeHeavyTask(ct)))
.Merge(maxConcurrent);
await query;
That's a lot easier to work with in my book.
Another option:
var block = new ActionBlock<int>(x => SomeHeavyTask(cancellationToken, x),
new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 20,
CancellationToken = cancellationToken
});
for (int i = 0; i < 100; i++)
await block.SendAsync(i, cancellationToken);
block.Complete();
await block.Completion;
You could use a SemaphoreSlim to limit the number of tasks that are working concurrently (you will still have taskLimit Tasks active, but only a limited number of those will be doing the heavy work simultaneously; I assume this is what you want).
This is best demonstrated with a sample console app. If you run this you'll see from the output that a maximum of 5 "heavy tasks" are active simultaneously.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
class Program
{
static async Task Main()
{
Console.WriteLine("Starting");
// Cancel after 30 seconds for demo purposes.
using var source = new CancellationTokenSource(TimeSpan.FromSeconds(30));
await RunAsync(source.Token);
Console.WriteLine("Stopped.");
Console.ReadLine();
}
public static async Task RunAsync(CancellationToken cancellationToken)
{
int taskLimit = 20;
int concurrencyLimit = 5;
var sem = new SemaphoreSlim(concurrencyLimit);
var tasks = new List<Task>();
try
{
for (int i = 0; i < taskLimit; i++)
{
int p = i; // Prevent modified closure.
tasks.Add(Task.Run(() => MyTask(cancellationToken, p, sem)));
}
await Task.WhenAll(tasks);
}
catch (OperationCanceledException)
{
Console.WriteLine("Task(s) were cancelled.");
}
catch (Exception e)
{
// Exception Handling
}
}
public static async Task MyTask(CancellationToken cancellationToken, int a, SemaphoreSlim sem)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
await sem.WaitAsync(cancellationToken);
try
{
someHeavyTask(cancellationToken, a);
}
finally
{
sem.Release();
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Task was cancelled.");
return;
}
catch (Exception e)
{
//Exception Handling
}
}
}
static int heavyTaskCount;
static void someHeavyTask(CancellationToken cancel, int a)
{
int n = Interlocked.Increment(ref heavyTaskCount);
Console.WriteLine("Starting heavy task. Number of simultaneous heavy tasks = " + n);
// Simulate work. Make the work for each task take varying time by using 'a' for the sleep.
for (int i = 0; i < 20 && !cancel.IsCancellationRequested; ++i)
{
Thread.Sleep(100 + a*10);
}
n = Interlocked.Decrement(ref heavyTaskCount);
Console.WriteLine("Finishing heavy task. Number of simultaneous heavy tasks = " + n);
}
}
}
The core of this is controlled by the semaphore in the code here:
await sem.WaitAsync(cancellationToken);
try
{
someHeavyTask(cancellationToken, a);
}
finally
{
sem.Release();
}
Like #mjwills said, you can use a C# semaphore to manage concurrent access to resources. (random example)
I do recommend looking at existing solutions first though. For example, Hangfire.
You can store its state inside SF if needed.

Task.Delay vs Thread.Sleep difference

private static async Task MainFunc()
{
var watch = System.Diagnostics.Stopwatch.StartNew();
List<Task<int>> list = new List<Task<int>>();
for (int i = 1; i <= 3; i++)
{
list.Add(TaskFunc(i));
}
var taskResult = await Task.WhenAll(list);
foreach (var item in taskResult)
{
Console.Write($"i= {item}.{ Environment.NewLine }");
}
list.Clear();
watch.Stop();
var elapsedMs1 = watch.ElapsedMilliseconds;
Console.WriteLine($"Total execution time: { elapsedMs1 }");
Console.WriteLine();
watch.Restart();
for (int i = 1; i <= 3; i++)
{
list.Add(Task.Run(() => ThreadFunc(i)));
}
var threadResult = await Task.WhenAll(list);
foreach (var item in threadResult)
{
Console.Write($"i= {item}.{ Environment.NewLine }");
}
watch.Stop();
var elapsedMs2 = watch.ElapsedMilliseconds;
Console.WriteLine($"Total execution time: { elapsedMs2 }");
}
private static async Task<int> TaskFunc(int i)
{
if (i == 1)
await Task.Delay(2000);
else if (i == 2)
await Task.Delay(1000);
else if (i == 3)
await Task.Delay(5000);
return i;
}
private static int ThreadFunc(int i)
{
if (i == 1)
Thread.Sleep(2000);
else if (i == 2)
Thread.Sleep(1000);
else if (i == 3)
Thread.Sleep(5000);
return i;
}
In this example there are two functions, TaskFunc and ThreadFunc, which are called from MainFunc seperately. I am curious as to why the second method doesn't seem to have any effect, and seems to be skipped. Not even the Thread.Sleep(...) seems to get executed.
As you can see, the total execution time for ThreadFunc is very short, even though the sleep timers should be same for both methods. Also, i is always set to 4. I suppose that the main thread is doing something wrong. Can someone explain what is going on here?
The issue here is scoping. The i you are using inside the Task.Run(() => TheadFunc(i)) is not a new integer, but rather, since it's a delegate, the value of i will only be retrieved once the delegate is executed.
This leads to i being 4 in all cases, since your for-loop increases it that much. But since you do not have an if-condition for a value of 4, it won't perform any delays with Thread.Sleep(...).

Is there a way to cancel execution of method in cancelled task?

I have a problem, that I could not find any answear yet. And this is my first project with use of threading and Tasks. When my task is cancelled, it keeps executing time consuming method.
Right now I have not idea how to stop execution of the method together with the task.
Below is a loop, that runs tasks. Every single task is running a ParseHorseData method, that runs also several other methods. Execution of them takes sometimes a lot of time.
After the task is cancelled, before await Task.WhenAll(tasks); is completed, it takes a lot of time.
So, as in the question, is there a way to cancel execution of method in cancelled task?
List<Task> tasks = new List<Task>();
int loopCounter = 0;
int taskCounter = 0;
//for all races in the file
for (int i = 0; i < _allRaces.Count; i ++)
{
int j = i;
if (TaskCancellation == true)
{
break;
}
Task task = Task.Run(async () =>
{
while (!_cancellationToken.IsCancellationRequested)
{
loopCounter++;
ProgressBarTick("Requesting historic data", loopCounter, _allRaces.Count, 0);
//if the race is from 2018
if (_allRaces[j].RaceDate.Year == 2018)
{
Category = _allRaces[j].RaceCategory;
Distance = _allRaces[j].RaceDistance.ToString();
//for all horses in the race
for (int h = 0; h < _allRaces[j].HorseList.Count; h++)
{
HorseDataWrapper horse = new HorseDataWrapper();
//TIME CONSUMING
horse = ParseHorseData(_allRaces[j].HorseList[h], _allRaces[j].RaceDate);
_allRaces[j].HorseList[h] = horse;
}
}
taskCounter++;
if (loopCounter >= _allRaces.Count)
{
ProgressBarTick("Testing on historic data", taskCounter, _allRaces.Count, 0);
}
}
}, _tokenSource.Token);
tasks.Add(task);
}
try
{
await Task.WhenAll(tasks);
}
catch (TaskCanceledException)
{
//
}
finally
{
_tokenSource.Dispose();
}
is there a way to cancel execution of method in cancelled task?
All cancellation is cooperative. The method being executed must pass the CancellationToken into the methods that it calls, and either:
Periodically poll for cancellation using ThrowIfCancellationRequested. This approach is more appropriate for CPU-bound loops.
Take an action on cancellation using Register. This approach is more appropriate for interfacing with non-CancellationToken-based cancellation systems.
In this case, it sounds like polling is appropriate. I strongly recommend polling via ThrowIfCancellationRequested and not IsCancellationRequested, because when a task is canceled, it should throw an OperationCanceledException when awaited. This is how the calling code knows it has been canceled.
Example:
Task task = Task.Run(async () =>
{
while (true)
{
_cancellationToken.ThrowIfCancellationRequested();
...
//for all horses in the race
for (int h = 0; h < _allRaces[j].HorseList.Count; h++)
{
_cancellationToken.ThrowIfCancellationRequested();
HorseDataWrapper horse = new HorseDataWrapper();
horse = ParseHorseData(_allRaces[j].HorseList[h], _allRaces[j].RaceDate);
_allRaces[j].HorseList[h] = horse;
}
...
}
});

How to launch N tasks (or threads) to run the same method that may return useless results?

I have a few stochastic functions that returns a Maybe<T>. When it produces a useful result, Maybe contains the result.
Maybe<T> is implemented like this:
public readonly struct Maybe<T> {
public readonly bool ContainsValue;
public readonly T Value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Maybe(bool containsValue, T value) {
ContainsValue = containsValue;
Value = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Maybe<T> Just(T value) {
return new Maybe<T>(
containsValue: true,
value: value);
}
public static Maybe<T> Empty { get; } = new Maybe<T>(
containsValue: false,
value: default
);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Maybe<T>(T value) => Maybe.Just(value);
}
I'd like to spawn create N tasks to run FuncThatMayFail(), with N = Environment.ProcessorCount. When the first task / thread actually get a useful result, it stops and tells the other tasks / threads to stop too.
My current approach is this:
public static Maybe<T> RunParallel<T>(int maximumRetries, Func<Maybe<T>> func) {
if (maximumRetries < 0)
throw new ArgumentOutOfRangeException(nameof(maximumRetries) + " must be >= 0");
if (func == null)
throw new ArgumentNullException(nameof(func));
var retries = 0;
var tasks = new Task<Maybe<T>>[Environment.ProcessorCount];
var finished = 0;
for (int i = 0; i < tasks.Length; i++) {
tasks[i] = Task.Run(() => {
while (true) {
if (retries >= maximumRetries || finished > 0)
return Maybe<T>.Empty;
var attempt = func();
if (attempt.ContainsValue) {
Interlocked.Increment(ref finished);
return attempt;
} else {
Interlocked.Increment(ref retries);
}
}
});
}
Task.WaitAny(tasks);
for (int i = 0; i < tasks.Length; i++) {
var t = tasks[i];
if (t.IsCompletedSuccessfully && t.Result.ContainsValue)
return t.Result;
}
return Maybe<T>.Empty;
}
I posted this on codereview asking for improvement suggestions and got none.
I feel this code is ugly and that there probably is a better way to do this.
Is there a more elegant (without using external libraries) to achieve this?
I'm using C# 7.2 targeting .Net Core 2.2
I've updated the code and posted it below. It's not tested but the answer is in there. You should be able to run as is but if not take what you need out of it.
First you need to add a CancellationTokenSource and pass the Token to the Task(s)
started so that you can signal them when to stop (from the frameworks
perspective).
Then you need to monitor that CancellationTokenSource yourself in the while loop to manually stop the tasks.
Task.WaitAny returns the index of the Task that was
completed so you don't need to iterate through them to find it.
You're also already returning Maybe<T>.Empty if the Task
ends without a result so no need to test ContainsValue; just
return the Result.
Code is below and documented where I made changes.
//Make a cancellation token source to signal other tasks to cancel.
CancellationTokenSource cts = new CancellationTokenSource();
for (int i = 0; i < tasks.Length; i++)
{
tasks[i] = Task.Run(() => {
while (!cts.IsCancellationRequested) //Monitor for the cancellation token source to signal canceled.
{
if (retries >= maximumRetries || finished > 0)
return Maybe<T>.Empty;
var attempt = func();
if (attempt.ContainsValue)
{
Interlocked.Increment(ref finished);
return attempt;
}
else
{
Interlocked.Increment(ref retries);
}
}
return Maybe<T>.Empty;
}, cts.Token); //Add the token to the task.
}
var completedTaskIndex = Task.WaitAny(tasks); //Task.WaitAny gives you the index of the Task that did complete.
cts.Cancel(); //Signal the remaining tasks to complete.
var completedTask = tasks[completedTaskIndex]; //Get the task that completed.
return completedTask.Result; //You're returning Maybe<T>.Emtpy from the Task if it fails so no need to check ContainsValue; just return the result.

Task repeating?

Previously, when making a looping task it would run perfectly the first time and fall apart because of the timing problems, such as the functions of one individual task would start running faster than another, even though it was programmed to come before that task.
So my idea to figure out a way to repeat the events of a task once the delays(times) have ended, in my head it would go something like this:
private async Task programCore()
{
int n = 1000;
for (int i = 0; i < n; )
{
FirstThing();
await Task.Delay(2100);
SecondThing();
await Task.Delay(2200);
ThirdThing();
await Task.Delay(2300);
}
////////////////////////////////////////////////////////////////////////////
else if (programCore.TasksHaveAllTimedOut) // Idealistic code.
{
programCore.Restart();
}
}
Is something like this possible? If not, is there a better way to do it?
How about this:
private async Task programCore()
{
int n = 1000;
for (int i = 0; i < n; )
{
await FirstThing();
await SecondThing();
await ThirdThing();
}
}
private async Task FirstThing()
{
// Do something here
}
private async Task SecondThing()
{
// Do something here
}
private async Task ThirdThing()
{
// Do something here
}
This has the effect that it runs FirstThing, waits for it to finish, then runs SecondThing, waits for it to finish, then finally runs ThirdThing and waits for it to finish before repeating the loop.
I believe #CodingGorilla has the right idea. Your code should look something like this (using CancellationTokens for timeouts):
private async Task programCore()
{
int n = 1000;
for (int i = 0; i < n; ++i)
{
var timeout1 = new CancellationTokenSource(2100);
await FirstThingAsync(timeout1.Token);
var timeout2 = new CancellationTokenSource(2200);
await SecondThingAsync(timeout2.Token);
var timeout2 = new CancellationTokenSource(2300);
await ThirdThingAsync(timeout3.Token);
}
}
The code above will raise an exception if any task exceeds its timeout, which may not be exactly what you want. You can catch the OperationCanceledException if you wish to handle this differently.

Categories