我正在查看VS2010并发分析器的输出,我注意到我正在围绕一些LINQ操作符获得一些线程争用。下面是引起争用的语句:
m_dictionary.PermutableSubunits.Select(subunit => subunit.Number).ToArray()
LINQ操作符阻塞了吗?我应该更小心地使用他们在一个任务,是作为并行foreach的一部分运行?
我假设您正在询问LINQ to Objects,因此代码中的Select调用对应于Enumerable.Select(..)。
LINQ to Objects操作符本身不会显式阻塞正在执行的线程。然而,它们确实会分配内存:例如,ToArray操作符会分配越来越大的数组来缓冲结果。
并且,内存分配可以导致线程阻塞。在分配内存时,CLR或操作系统可能需要获得一些锁,以便定位一块空闲内存。更重要的是,CLR可能决定在任何时候分配内存时运行垃圾收集(GC),这可能导致严重的线程阻塞。
如果服务器GC非常适合您的应用程序,您可以尝试打开它,看看吞吐量是否有所提高。此外,您经常可以编写非LINQ代码,执行比LINQ到对象查询更少的内存分配。在您的特定示例中,我相信LINQ to Objects将开始将结果生成为一个小数组,在结果不适合时分配一个更大的数组。您的自定义实现可以在一开始就分配合适大小的数组,从而避免一堆不必要的分配。
它不应该阻塞,但是如果你使用的是Linq-to-SQL,如果你的查询需要很长时间来执行,它可能需要很长时间…一般来说,任何时候使用多线程都应该"更加小心",或者像他们说的那样:"小心线程!"
然而,如果您遇到争用问题,那么您应该真正分析您实际在做什么。Linq不是线程安全的,所以如果你在一个实体上执行读/写操作,而这个实体有可能从另一个线程改变,那么你应该正确地同步。