跟踪多步骤任务的进度

cir*_*ner 4 c# task-parallel-library c#-4.0

我正在开发一个向客户端公开Web服务的简单服务器.某些请求可能需要很长时间才能完成,并且在逻辑上分为多个步骤.对于此类请求,需要在执行期间报告进度.此外,可以在前一个请求完成之前启动新请求,并且要求两个请求同时执行(除非某些特定于系统的限制).

我想让服务器将TaskId返回给它的客户端,并让客户端使用TaskId跟踪请求的进度.我认为这是一个很好的方法,我留下了如何管理任务的问题.

从来没有使用过TPL,我认为这是解决这个问题的好方法.实际上,它允许我同时运行多个任务而无需手动管理线程.我甚至可以使用ContinueWith相对容易地创建多步骤任务.

但是,我无法想出一个跟踪任务进度的好方法.我意识到,当我的请求包含一个"步骤"时,该步骤必须合作报告其状态.这是我现在宁愿避免的.但是,当请求包含多个步骤时,我想知道当前正在执行哪个步骤并相应地报告进度.我能想到的唯一方法是非常无聊:

Task<int> firstTask = new Task( () => { DoFirstStep(); return 3.14; } );
firstTask.
ContinueWith<int>( task => { UpdateProgress("50%"); return task.Result; } ).
ContinueWith<string>( task => { DoSecondStep(task.Result); return "blah"; }.
ContinueWith<string>( task => { UpdateProgress("100%"); return task.Result; } ).
Run Code Online (Sandbox Code Playgroud)

即使这不完美,因为我希望Task存储自己的进度,而不是让UpdateProgress更新一些已知的位置.此外,在添加新步骤时必须更改大量位置的明显缺点(因为现在进度为33%,66%,100%而不是50%,100%).

有没有人有一个好的解决方案?

谢谢!

Ade*_*ler 5

这不是Task Parallel Library完全支持的场景.

您可能会考虑将进度更新提供给队列并在另一个任务上读取它们的方法:

static void Main(string[] args)
{
    Example();
}

static BlockingCollection<Tuple<int, int, string>> _progressMessages = 
    new BlockingCollection<Tuple<int, int, string>>();

public static void Example()
{
    List<Task<int>> tasks = new List<Task<int>>();

    for (int i = 0; i < 10; i++)
        tasks.Add(Task.Factory.StartNew((object state) =>
            {
                int id = (int)state;
                DoFirstStep(id);
                _progressMessages.Add(new Tuple<int, int, string>(
                    id, 1, "10.0%"));
                DoSecondStep(id);
                _progressMessages.Add(new Tuple<int, int, string>(
                    id, 2, "50.0%"));

                // ...

                return 1;
            },
            (object)i
            ));

    Task logger = Task.Factory.StartNew(() =>
        {
            foreach (var m in _progressMessages.GetConsumingEnumerable())
                Console.WriteLine("Task {0}: Step {1}, progress {2}.",
                m.Item1, m.Item2, m.Item3);
        });


    List<Task> waitOn = new List<Task>(tasks.ToArray());
    waitOn.Add(logger);
    Task.WaitAll(waitOn.ToArray());
    Console.ReadLine();
}

private static void DoSecondStep(int id)
{
    Console.WriteLine("{0}: First step", id);
}

private static void DoFirstStep(int id)
{
    Console.WriteLine("{0}: Second step", id);
}
Run Code Online (Sandbox Code Playgroud)

此示例不显示您的任务可能需要长时间运行的取消,错误处理或帐户.长时间运行的任务对调度程序提出了特殊要求.有关这方面的更多讨论,请访问http://parallelpatterns.codeplex.com/,下载书籍草稿并查看第3章.

这只是在这种情况下使用任务并行库的方法.TPL可能不是这里最好的方法.

如果您的Web服务在ASP.NET(或类似的Web应用程序服务器)中运行,那么您还应该考虑使用线程池中的线程执行任务而不是服务Web请求的可能影响:

Task Parallel Library如何在终端服务器或Web应用程序中进行扩展?