具有队列和超时问题的TaskScheduler

时间:2016-03-02 08:16:57

标签: c# multithreading task-parallel-library taskscheduler

我试图实施的内容:

任务调度程序,它将任务排入队列并并行运行指定的数量,而其他任务调度程序则在队列中等待启动。每个任务都有超时,在任务运行时开始计数,如果超过该时间任务被取消并抛出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的第一步,所以请原谅我身边的任何愚蠢

2 个答案:

答案 0 :(得分:2)

Add中,您拨打.TimeoutAfter(timeoutInMilliseconds, cts)。这意味着一旦创建任务就会立即启动超时。我认为你的意图是只在任务开始执行时才启动超时。

由于第一个任务的时间超过了超时时间,所以当其他任务轮到他们时,所有其他任务都已超时。

答案 1 :(得分:1)

不是完美的解决方案,但是我能想到的最好的解决方案:

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;
}

简而言之:当从队列启动任务时,它会在默认任务调度程序上创建子任务(因为另一个只会将其排队),然后我们等待一定的时间并调用回调函数。不知道这有多糟糕。