【问题标题】:Do LINQ Operators block at all?LINQ 运算符是否完全阻塞?
【发布时间】:2011-04-16 19:19:32
【问题描述】:

我正在查看 VS2010 Concurrency Profiler 的输出,我注意到我在一些 LINQ 运算符周围遇到了一些线程争用。以下是引起争用的语句:

m_dictionary.PermutableSubunits.Select(subunit => subunit.Number).ToArray()

LINQ 运算符会阻塞吗?在作为 Parallel.ForEach 的一部分运行的任务中使用它们时我应该更加小心吗?

【问题讨论】:

  • 您的意思是 LINQ 运算符,我希望它是单线程的还是使用线程池的 PLINQ 运算符?我不确定我是否理解您的问题。
  • 我的意思是 LINQ 运算符(如上面的 Select() 和 ToArray()),而不是 PLINQ(请注意上面没有 .AsParallel())。上面的代码在由 Parallel.ForEach 方法启动的 Task 中运行。这就是你想知道的吗?

标签: linq concurrency task-parallel-library parallel-extensions


【解决方案1】:

我假设您询问的是 LINQ to Objects,因此代码中的 Select 调用对应于 Enumerable.Select(..)。

LINQ to Objects 运算符本身不会显式阻止正在执行的线程。但是,它们确实会分配内存:例如,ToArray 运算符将分配越来越大的数组以缓冲结果。

而且,内存分配可能导致线程阻塞。当您分配内存时,CLR 或操作系统可能需要获取一些锁才能找到一块空闲内存。更重要的是,CLR 可能会决定在您分配内存的任何时候运行垃圾回收 (GC),这可能会导致严重的线程阻塞。

如果服务器 GC 非常适合您的应用程序,您可以尝试打开它,看看吞吐量是否有所提高。此外,您通常可以编写比 LINQ to Objects 查询执行更少内存分配的非 LINQ 代码。在您的特定示例中,我相信 LINQ to Objects 将开始将结果生成到一个小数组中,并在结果不适合时分配一个更大的数组。您的自定义实现可能能够在开始时分配正确大小的数组,避免大量不必要的分配。

【讨论】:

  • 谢谢伊戈尔,你是对的,我正在使用 Linq to Objects,我也在使用服务器 GC。我注意到在将某些从 Linq 中受到很多影响的代码重写为普通的旧 for 循环时,性能会有所提高,所以我想我会暂时继续这样做。
【解决方案2】:

它不应该阻塞,但是如果您使用的是 Linq-to-SQL,如果您的查询需要很长时间才能执行,那么它可能需要特别长的时间......一般来说,任何时候您都在使用多线程做某事你应该“更加小心”或者正如他们所说:“小心穿线!”

但是,如果您遇到争用问题,那么您应该真正分析您实际在做什么。 Linq 不是线程安全的,因此如果您正在对可能从另一个线程更改的实体执行读/写操作,那么您应该正确同步。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-16
    • 2011-04-09
    • 2017-01-25
    • 1970-01-01
    相关资源
    最近更新 更多