具有队列和超时问题的 TaskScheduler
TaskScheduler with queue and timeout issue
我要实现的是:
任务调度程序,将任务排入队列并并行运行指定数量,而其他任务则在队列中等待开始。每个任务都有超时,当任务运行时开始计数,如果超过该时间,任务将被取消并抛出 TimeoutException,它在 ContinueWith 中处理(或在其后立即运行的某些任务)。任务应该可以被用户取消。
我得到的:
当第一个任务失败时,所有其他任务都会立即失败。
这是我的任务计划程序的完整代码(取自 MSDN,并进行了一些修改):
http://pastebin.com/KSMbDTH5。 (有问题的函数在第 161 行)
这是用法示例:
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);
}
它应该如何工作:(我们在 2 秒后强制超时事件,因此两个任务都不会完成)
对于 MaxDegreeOfParallelism = 1:
Start;
(Wait 2sec)
Fail;
Start;
(Wait 2sec)
Fail;
....
对于 MaxDegreeOfParallelism = 2:
Start;
Start;
(Wait 2sec)
Fail;
Fail;
Start;
Start;
(Wait 2sec)
Fail;
Fail;
....
工作原理:
Start;
(Wait 2sec)
Fail;
Fail;
Fail;
Fail;
...
(MaxDegreeOfParallelism = 1,其余的也很乱)
注意:这是我使用 TPL 的第一步,请原谅我的愚蠢行为
在 Add
你调用 .TimeoutAfter(timeoutInMilliseconds, cts)
。这意味着超时会在您创建任务后立即开始。我认为您的意图是仅在任务开始执行时才开始超时。
由于第一个任务花费的时间比超时时间长,所有其他任务在轮到它们时都已经超时。
不是完美的解决方案,但我能想到的最好的解决方案:
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;
}
简而言之:当任务从队列中启动时,它会在默认任务调度程序上创建子任务(因为另一个任务调度程序只会将其入队),然后我们等待一定时间并调用回调函数。不知道这有多糟糕。
我要实现的是:
任务调度程序,将任务排入队列并并行运行指定数量,而其他任务则在队列中等待开始。每个任务都有超时,当任务运行时开始计数,如果超过该时间,任务将被取消并抛出 TimeoutException,它在 ContinueWith 中处理(或在其后立即运行的某些任务)。任务应该可以被用户取消。
我得到的:
当第一个任务失败时,所有其他任务都会立即失败。
这是我的任务计划程序的完整代码(取自 MSDN,并进行了一些修改):
http://pastebin.com/KSMbDTH5。 (有问题的函数在第 161 行)
这是用法示例:
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);
}
它应该如何工作:(我们在 2 秒后强制超时事件,因此两个任务都不会完成)
对于 MaxDegreeOfParallelism = 1:
Start;
(Wait 2sec)
Fail;
Start;
(Wait 2sec)
Fail;
....
对于 MaxDegreeOfParallelism = 2:
Start;
Start;
(Wait 2sec)
Fail;
Fail;
Start;
Start;
(Wait 2sec)
Fail;
Fail;
....
工作原理:
Start;
(Wait 2sec)
Fail;
Fail;
Fail;
Fail;
...
(MaxDegreeOfParallelism = 1,其余的也很乱)
注意:这是我使用 TPL 的第一步,请原谅我的愚蠢行为
在 Add
你调用 .TimeoutAfter(timeoutInMilliseconds, cts)
。这意味着超时会在您创建任务后立即开始。我认为您的意图是仅在任务开始执行时才开始超时。
由于第一个任务花费的时间比超时时间长,所有其他任务在轮到它们时都已经超时。
不是完美的解决方案,但我能想到的最好的解决方案:
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;
}
简而言之:当任务从队列中启动时,它会在默认任务调度程序上创建子任务(因为另一个任务调度程序只会将其入队),然后我们等待一定时间并调用回调函数。不知道这有多糟糕。