按顺序处理任务

时间:2016-09-20 21:27:31

标签: c# task-parallel-library task

我有一个基类JobBase,它有一个返回任务的方法Execute()。有几个类继承自JobBase并执行一些异步操作。我需要将继承类的实例放入列表并按顺序执行,因此job1,job2,job3等job2将在作业1完成时启动,作业3将在job2完成时启动等等。

因此队列可能包含任意数量的作业。 例如:job1下载一些内容,job2读取内容。在另一种情况下,job1下载一些内容,job2下载一些其他内容,job3读取内容等。

示例

// The base class
public abstract class JobBase
{
    public abstract Task Execute();
}

// inherited jobs
public class Job1 : JobBase
{
    public override Task Execute()
    {
        // this one: return Task.Run()
        // or this one?: 
        Task task = new Task(async () =>
        {
            await Task.Delay(2000);
        });
        return task; 
    }
}

public class Job2 : JobBase
{
    public override Task Execute()
    {
        Task task = new Task(async () =>
        {
            await Task.Delay(1000);
        });
        return task; 
    }
}

主程序:

private static void Main(string[] args)
{
    var jobs = new List<JobBase>();
    jobs.Add(new Job1());
    jobs.Add(new Job2());

    List<Task> tasks = new List<Task>();
    for (int i = 0; i < jobs.Count; i++)
    {
        var jobNr = i;
        Task task = jobs[jobNr].Execute();
        tasks.Add(task);

        task.Start();
    }

    Console.WriteLine("Starting WaitAll");
    Task.WaitAll(tasks.ToArray());

    Console.WriteLine("Finished");
    Console.ReadKey();
}

实际上,任务已执行,我无法控制它们执行(或完成)的顺序。另外,如果前一个工作成功,我想得到每个工作的结果(bool)并继续下一个工作。

我阅读并尝试了SequentialTask​​Scheduler,解决方案和TaskCompletionSource,关于Processing tasks as they complete的教程。 由于工作(任务)的数量未知,我无法使用ContinueWith。

请查看以下示例及其输出。为简单起见,有一个类Job继承自JobBase,它接受​​一个索引和一个延迟值:

public class Job : JobBase
{
    private readonly int _index;
    private readonly int _delay;

    public Job(int index, int delay)
    {
        _index = index;
        _delay = delay;
    }

    public override Task Execute()
    {
        Task task = new Task(async () =>
        {
            Console.WriteLine("Job {0} before delay", _index);
            await Task.Delay(_delay);
            Console.WriteLine("Job {0} after delay", _index);
        });
        return task;
    }
}

将一些实例添加到任务列表中,例如

jobs.Add(new Job(1, 3000));
jobs.Add(new Job(2, 2000));
jobs.Add(new Job(3, 1000));

我得到以下结果:

Starting WaitAll
Job 2 before delay
Job 3 before delay
Job 1 before delay
Finished
Job 3 after delay
Job 2 after delay
Job 1 after delay

(Job3首先结束,因为延迟小于job2等)

期望的结果是:

Starting WaitAll
Job 1 before delay
Job 1 after delay
Job 2 before delay
Job 2 after delay
Job 3 before delay
Job 3 after delay
Finished

顺序运行异步任务的最佳方法是什么?我可以自由地更改Execute方法的签名,例如返回Task或其他任何内容。

2 个答案:

答案 0 :(得分:0)

试试这个:

async void Main(string[] args)
{
    var jobs = new List<JobBase>();
    jobs.Add(new Job(1, 3000));
    jobs.Add(new Job(2, 2000));
    jobs.Add(new Job(3, 1000));

    for (int i = 0; i < jobs.Count; i++)
    {
        var t = jobs[i].Execute();
        await t;
    }

    Console.WriteLine("Finished");
    Console.ReadLine();
}

请注意,我们不再使用

 Task.WaitAll(tasks.ToArray());

因为这允许任务并行运行。我们只是在循环中等待他们。

将Job类修改为:

public class Job : JobBase
{
    private readonly int _index;
    private readonly int _delay;

    public Job(int index, int delay)
    {
        _index = index;
        _delay = delay;
    }

    public override Task Execute()
    {
        Task task = Task.Run(async () =>
        {
            Console.WriteLine("Job {0} before delay", _index);
            await Task.Delay(_delay);
            Console.WriteLine("Job {0} after delay", _index);
        });
        return task;
    }
}

请注意

的更改
Task task = new Task(async () =>

Task task = Task.Run(async () =>

答案 1 :(得分:0)

  

所以队列......

什么队列?您当前的代码使用DROP + CREATE + INSERT INTO List<T>,如果您知道等待了多少任务,那就没问了。它不能作为“任务跑步者”,因为不可能打断Task.WaitAll并说“哦,你现在有一项新任务”。

听起来像你正在寻找的是ActionBlock<T>

WaitAll
默认情况下,

private static void Main(string[] args) { var jobs = new ActionBlock<JobBase>(x => x.Execute()); jobs.Add(new Job1()); jobs.Add(new Job2()); jobs.Complete(); Console.WriteLine("Starting Wait"); Task.WaitAll(jobs.Completion.Wait()); // only using Wait because this code is in Main Console.WriteLine("Finished"); Console.ReadKey(); } 将一次执行一项任务。

  

此外,如果上一份工作成功,我希望获得每项工作的结果(bool)并继续下一份工作。

如果您报告包含例外的错误,则ActionBlock<T>将停止处理第一个失败的作业。