Parallel.ForEach迭代中的动作超时

时间:2014-03-25 22:01:28

标签: c# task-parallel-library c#-5.0 parallel.foreach

我的代码中有类似的内容:

Parallel.ForEach(myList, new ParallelOptions { MaxDegreeOfParallelism = 4 }, item =>
{
    Process(item);
});

问题是我在Process()方法中做了很多事情(连接到文件共享,解析文件,保存到数据库等),我担心在这个过程中可能会出错迭代永远不会完成...... 这可能发生吗?

有没有办法为Process()方法设置超时,以避免最终拥有僵尸线程?

更新

我发现设置超时的最简单方法是在CancellationTokenSource上添加毫秒或在任务上调用Wait()方法。

选项#1

Parallel.ForEach(myList, new ParallelOptions { MaxDegreeOfParallelism = 4 }, item =>
{
    var cts = new CancellationTokenSource(2000);
    Task task = Task.Factory.StartNew(() => Process(item), cts.Token);
});

选项#2

Parallel.ForEach(myList, new ParallelOptions { MaxDegreeOfParallelism = 4 }, item =>
{
    Task task = Task.Factory.StartNew(() => Process(item));
    task.Wait(2000);
});

问题是这些选项都无法取消Process()方法。我是否需要在Process()方法中检查某些内容?

3 个答案:

答案 0 :(得分:2)

考虑将CancellationToken添加到您的代码中。这样,您可以在任何时候正确取消所有操作。

然后,您可以使用CancelAfter()方法。

答案 1 :(得分:2)

我最终结合了两个选项。它有效,但我不知道这是否是正确的方法。

解决方案:

        Parallel.ForEach(myList, new ParallelOptions { MaxDegreeOfParallelism = 4 }, item =>
        {
                var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
                var token = tokenSource.Token;

                Task task = Task.Factory.StartNew(() => Process(item, token), token);
                task.Wait();
        });

并在Process()我多次检查取消:

    private void Process(MyItem item, CancellationToken token)
    {
        try
        {
            if (token.IsCancellationRequested)
                token.ThrowIfCancellationRequested();

            ...sentences

            if (token.IsCancellationRequested)
                token.ThrowIfCancellationRequested();

            ...more sentences

            if (token.IsCancellationRequested)
                token.ThrowIfCancellationRequested();

            ...etc
        }
        catch(Exception ex)
            Console.WriteLine("Operation cancelled");

答案 2 :(得分:1)

我的结果略有不同。就我而言,检查CancellationTokenProcess()内,由于检查之间可能存在长时间运行的语句,因此无效。例如,如果我的超时是5秒,并且单个语句花了100秒...我不会知道,直到该语句完成,然后被if (token.IsCancellationRequested)检测到。

这就是我最终做的事情

     Parallel.ForEach(myList, (item) =>
     {
         Task task = Task.Factory.StartNew(() =>
         {
             Process(item));
         });

         if(task.Wait(10000)) // Specify overall timeout for Process() here
             Console.WriteLine("Didn't Time out. All's good!"); 
         else
             Console.WriteLine("Timed out. Leave this one and continue with the rest"); 
     });

然后在Process()方法中,我添加了对可能长时间运行的语句的进一步检查,以允许它优雅地处理超时(尽可能多)。因此,只有在最坏的情况下,Process()必须由Task.Wait()过早地停止。

    private void Process(MyItem item)
    {
      ...
        cmd.CommandTimeout = 5; // The total of timeouts within Process() were 
                                // set to be less than the total Task.Wait duration.
                                // Unfortunately some potentially long running methods 
                                // don't have a built in timeout.
      ...
    }