在 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);
我正在捕捉时间,惊讶地发现时间如下:
- 以上场景:6 秒(6000 毫秒)
- 相同的代码,运行 按顺序而不是 2 个任务:50 毫秒
- 相同的代码,但 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 次,您可能会得到截然不同的结果。
因此,我的最后一个论点是您尝试衡量并行代码,但很难做到正确。我不建议尽可能多地使用并行的东西,因为它会导致性能下降,如果你不确切地知道,你在做什么。
在 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);
我正在捕捉时间,惊讶地发现时间如下:
- 以上场景:6 秒(6000 毫秒)
- 相同的代码,运行 按顺序而不是 2 个任务:50 毫秒
- 相同的代码,但 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 次,您可能会得到截然不同的结果。
因此,我的最后一个论点是您尝试衡量并行代码,但很难做到正确。我不建议尽可能多地使用并行的东西,因为它会导致性能下降,如果你不确切地知道,你在做什么。