2010-08-03 81 views
4

我正在开发一个向客户端公开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; }). 

即使这是不完美的,因为我想任务来存储,而不必更新的UpdateProgress一些已知的位置了自己的进步。另外,当增加一个新步骤时(因为现在的进度是33%,66%,100%而不是50%,100%),它具有不得不改变很多地方的明显缺点。

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

谢谢!

回答

4

这是不是真的那么任务并行,充分

你可能会在那里你是否厌倦进度更新到队列中,并在另一个任务读取他们考虑的一个方法库支持的方案。:

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

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

这只是在类似情况下使用任务并行库的一种方法。 TPL可能而不是是最好的办法。

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

How does Task Parallel Library scale on a terminal server or in a web application?

+0

感谢您的详细回复!我同意TPL可能不是这里最好的方法。我希望有机会使用它,但它可能要等待另一天... – 2010-08-05 05:05:04

0

我不认为你正在寻找的解决方案将涉及任务API。或者至少,不是直接。它不支持百分比完成的概念,并且Task/ContinueWith函数需要参与该逻辑,因为它的数据只在该级别可用(只有ContinueWith的最终调用才能知道完成百分比,即使这样做,算法上也是一种猜测,因为它肯定不知道一个任务是否需要比另一个任务更长的时间。我建议你创建自己的API来执行此操作,可能利用Task API做实际工作

+0

是的,在发布使用ContinueWith(...)后,我意识到实际上没有一个包含所有其他任务的大任务,而是一系列任务,因此任何单个任务都无法知道它。谢谢你的帮助! – 2010-08-05 05:03:52