Reputation: 373
What I'm trying to implement:
Task scheduler that enqueues tasks and runs specified amount in parallel, while others wait in queue to get started. Each task has timeout that starts counting when the task runs and if exceeds that time the task is canceled and throws TimeoutException, which is handled in ContinueWith (or some task that runs immediately after). Tasks should be cancelable by user.
What I get:
When the first task fails all others fail too instantly.
Here is the full code of my Task Scheduler (taken from MSDN with some modifications):
http://pastebin.com/KSMbDTH5. (the function in question is on line 161)
Here is example usage:
var taskTokens = new List<CancellationToken>();
var factory = new TaskScheduleFactory(new ParallelOptions() { MaxDegreeOfParallelism = 1 }); //for the purpose of testing, supposed to work and with higher values
for (var i = 0; i < 10; i++)
{
//TaskScheduleFactory.cs, line 161
var taskToken = factory.Add(
(token) => //Task
{
Console.WriteLine("Start");
Thread.Sleep(5000);
if (token.IsCancellationRequested) return; //Cancelled by timeout
Console.WriteLine("This should not print");
},
(task) => //ContinueWith
{
if (task.IsFaulted)
{
Console.WriteLine("Fail");
}
else if (!task.IsCompleted)
{
Console.WriteLine("Not completed");
}
else Console.WriteLine("Done");
},
2000 //Timeout
);
taskTokens.Add(taskToken);
}
How it is supposed to work: (We force timeout event after 2sec so neither task would complete)
For MaxDegreeOfParallelism = 1:
Start;
(Wait 2sec)
Fail;
Start;
(Wait 2sec)
Fail;
....
For MaxDegreeOfParallelism = 2:
Start;
Start;
(Wait 2sec)
Fail;
Fail;
Start;
Start;
(Wait 2sec)
Fail;
Fail;
....
How it's working:
Start;
(Wait 2sec)
Fail;
Fail;
Fail;
Fail;
...
(for MaxDegreeOfParallelism = 1, the rest are mess too)
Note: These are my first steps with TPL, so excuse any stupidity from my side
Upvotes: 2
Views: 1446
Reputation: 373
Not the perfect solution, but the best that I could think of:
public CancellationTokenSource Add(Action<CancellationToken> action, Action<Task> callback, int timeoutInMilliseconds)
{
var cts = new CancellationTokenSource();
Instance.StartNew(() =>
{
cts.CancelAfter(timeoutInMilliseconds);
var task = Task.Factory.StartNew(() => action(cts.Token), cts.Token, TaskCreationOptions.AttachedToParent|TaskCreationOptions.LongRunning, TaskScheduler.Default);
try
{
task.Wait(timeoutInMilliseconds, cts.Token);
}
catch (OperationCanceledException) { }
callback(task);
}, cts.Token);
return cts;
}
In short: When task is started from the queue it creates child task on the default task scheduler (because the other one will just enqueue it), then we wait for certain amount of time and call callback function. Not sure how bad this is.
Upvotes: 1
Reputation: 171206
In Add
you call .TimeoutAfter(timeoutInMilliseconds, cts)
. This means that the timeout starts as soon as you create the task. I think your intention was to only start the timeout when the task starts executing.
Since the first task takes longer than the timeout all other tasks are already timed out when they get their turn.
Upvotes: 2