在 C# 中创建新的单独任务时,运行 LINQ 查询 .asparallel 花费的时间增加

Increase in time taken to run LINQ query .asparallel when creating new separate tasks in C#

在 LINQ 查询中,我使用了 .AsParallel,如下所示:

var completeReservationItems = from rBase in reservation.AsParallel()
                                       join rRel in relationship.AsParallel() on rBase.GroupCode equals rRel.SourceGroupCode
                                       join rTarget in reservation.AsParallel() on rRel.TargetCode equals rTarget.GroupCode
                                       where rRel.ProgramCode == programCode && rBase.StartDate <= rTarget.StartDate && rBase.EndDate >= rTarget.EndDate
                                       select new Object
                                       {
                                           //Initialize based on the query
                                       };

然后,我创建了两个单独的任务并运行它们并行,将相同的列表传递给两种方法,如下所示:

            Task getS1Status = Task.Factory.StartNew(
            () =>
            {
                RunLinqQuery(params);
            });
        Task getS2Status = Task.Factory.StartNew(
            () =>
            {
                RunLinqQuery(params);
            });

        Task.WaitAll(getS1Status, getS2Status);

我正在捕捉时间,惊讶地发现时间如下:

  1. 以上场景:6 秒(6000 毫秒)
  2. 相同的代码,运行 按顺序而不是 2 个任务:50 毫秒
  3. 相同的代码,但 LINQ 中没有 .AsParallel():50 毫秒

我想了解为什么在上述情况下需要这么长时间。

将此作为答案发布只是因为我有一些代码要显示。

首先,我不知道 AsParallel() 会创建多少个线程。文档什么都不说 https://msdn.microsoft.com/en-us/library/dd413237(v=vs.110).aspx

想象一下下面的代码

void RunMe()
{
    foreach (var threadId in Enumerable.Range(0, 100)
                            .AsParallel()
                            .Select(x => Thread.CurrentThread.ManagedThreadId)
                            .Distinct())
        Console.WriteLine(threadId);
}

我们将看到多少线程的 ID?对我来说每次都会看到不同数量的线程,示例输出:

30 // only one thread!

下次

27 // several threads
13
38
10
43
30

我认为,线程数取决于当前调度程序。我们总是可以通过调用 WithDegreeOfParallelism (https://msdn.microsoft.com/en-us/library/dd383719(v=vs.110).aspx) 方法来定义最大线程数,例如

void RunMe()
{
    foreach (var threadId in Enumerable.Range(0, 100)
                            .AsParallel()
                            .WithDegreeOfParallelism(2)
                            .Select(x => Thread.CurrentThread.ManagedThreadId)
                            .Distinct())
        Console.WriteLine(threadId);
}

现在,输出最多包含 2 个线程。

7
40

为什么这么重要?正如我所说,线程数可以直接影响性能。 但是,这并不是所有问题。在您的 1 场景中,您正在创建新任务(将在线程池内执行并可能增加额外的开销),然后您正在调用 Task.WaitAll。查看它的源代码 https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs,72b6b3fa5eb35695 ,我确信那些 for 循环任务会增加额外的开销,并且在 AsParallel 将在第一个任务中占用太多线程的情况下,下一个任务可以连续开始。此外,这可能会发生,因此,如果您 运行 您的 1 场景 1000 次,您可能会得到截然不同的结果。

因此,我的最后一个论点是您尝试衡量并行代码,但很难做到正确。我不建议尽可能多地使用并行的东西,因为它会导致性能下降,如果你不确切地知道,你在做什么。