2016-02-09 70 views
4

下面是一个示例PLINQ查询我在一个Windows服务中定期运行:PLINQ查询中的评估顺序是什么?

var resultList = new List<Task<SendMailResult>>(); 

try 
{ 
    resultList = emailsToSend 
     .AsParallel().WithDegreeOfParallelism(10) 
     .Select(async e => 
     { 
      bool bSuccess = false; 

      if (await MailHelper.SendMailAsync(e.sTo, e.sSubject, e.sHTML) == true) 
      { 
       bSuccess = true; 
      } 

      return new SendMailResult 
      { 
       succeeded = bSuccess, 
       resultid = e.id 
      }; 
     }).ToList(); 

    Task.WaitAll(resultList.ToArray()); 
} 
catch (AggregateException aggEx) 
{ 
    foreach (var ex in aggEx.InnerExceptions) 
     Console.Out.WriteLine(ex.Message); 
} 

我的问题是 - 如果一个例外是在内部匿名async Func<EmailToSend, Task<SendMailResult>>被抛出(在调用MailHelper.SendMailAsync()最有可能的),然后调用AggregateException处理程序 - 以下.ToList()已被调用在任何点?

换句话说,是否有可能某些任务成功完成,直到没有任何任务,并且在被捕获到AggregateException处理程序之后的下面的代码片段中,我可能会得到一个带有非0 Count的resultList?或者,异常意味着从不调用ToList(),并且如果引发异常,resultList将始终为空?

我意识到我与鲨鱼在使用这种多线程能力的情况下游泳时没有充分理解其含义。因此,这个问题!谢谢。

回答

1

.ToList()将在任何时候被调用,因为Selectlaziness和结果列表将包含所有任务。
执行将以.ToList()方法调用开始。 Select将仅提供IEnumerable,可枚举列表。
如果某些任务失败,出现异常,您将捕获它们,毕竟您将获得任务列表,其中某些任务的状态为Faulted

编辑:
抛出异常将被Task.WaitAll方法调用被触发,所以名单将之前创建,将有确切的项目算作初始集合。

更新:
关于Task.WaitAll内部结构:
如果你想知道它是如何工作的,你可以看source code
首先它collects all incompleted tasks然后waits为这些任务来完成。
然后它从所有任务collects all inner exceptions,然后提出它作为单一AggregateException

+0

有趣!因此,在*执行上面的代码之后,* *有保证*,那么resultList将是一个任务列表,在Counts中等于emailsToSend IEnumerable? – oflahero

+1

是的。因为异常将仅在'Task.WaitAll'调用中抛出。它将在列表创建后调用,因此列表将具有预期的长度。 – Kote

+0

因此,任何进一步的处理都应该检查如下内容: 'resultList.Where(t => t.IsFaulted == false).Select(t => t.Result).Where(...',right? – oflahero