C# TypeDescriptor.GetProperties()始终返回相同的PropertyDescriptorCollection引用。导致并行处理问题
前言:我正在将一个库移到并行处理数据上,而C# TypeDescriptor.GetProperties()始终返回相同的PropertyDescriptorCollection引用。导致并行处理问题,c#,multithreading,reflection,parallel-processing,contention,C#,Multithreading,Reflection,Parallel Processing,Contention,前言:我正在将一个库移到并行处理数据上,而PropertyDescriptor.GetValue()上的线程之间存在85%以上的争用,使得多核上的并行处理相同,有时甚至比单核上的处理更糟糕 问题: public static void Test() { var class1 = new ExampleClass(); var class2 = new ExampleClass(); var test1 = TypeDescript
PropertyDescriptor.GetValue()
上的线程之间存在85%以上的争用,使得多核上的并行处理相同,有时甚至比单核上的处理更糟糕
问题:
public static void Test()
{
var class1 = new ExampleClass();
var class2 = new ExampleClass();
var test1 = TypeDescriptor.GetProperties(typeof(ExampleClass));
var test2 = TypeDescriptor.GetProperties(typeof(ExampleClass));
Console.WriteLine(Object.ReferenceEquals(test1, test2));
int i = 0;
foreach(PropertyDescriptor item in test1)
{
Console.WriteLine(Object.ReferenceEquals(test1[i], test2[i]));
i++;
}
}
调用TypeDescriptor.GetProperties(Type)
时,它似乎返回了对相同PropertyDescriptorCollection
的相同引用,该引用对所有相同的PropertyDescriptor
对象具有相同的引用。这是有问题的,因为我需要为每个线程设置新的PropertyDescriptor
。共享相同的资源会导致线程不断地相互阻塞
并发分析输出:
粗略示例:
public static void Test()
{
var class1 = new ExampleClass();
var class2 = new ExampleClass();
var test1 = TypeDescriptor.GetProperties(typeof(ExampleClass));
var test2 = TypeDescriptor.GetProperties(typeof(ExampleClass));
Console.WriteLine(Object.ReferenceEquals(test1, test2));
int i = 0;
foreach(PropertyDescriptor item in test1)
{
Console.WriteLine(Object.ReferenceEquals(test1[i], test2[i]));
i++;
}
}
在这种情况下,每个都将返回true。表明它们都指向相同的对象,并且没有区别。当多个线程在同一PropertyDescriptor
对象上调用GetValue()
时,这会导致严重的性能问题
如何为每个对象属性获取全新的PropertyDescriptor
对象
注意:通过
myType.GetProperties()
检索的PropertyInfo
也会出现同样的问题。PropertyInfo
s返回对相同对象的引用,因此即使这样也在执行缓存
其他信息/发现:
public static void Test()
{
var class1 = new ExampleClass();
var class2 = new ExampleClass();
var test1 = TypeDescriptor.GetProperties(typeof(ExampleClass));
var test2 = TypeDescriptor.GetProperties(typeof(ExampleClass));
Console.WriteLine(Object.ReferenceEquals(test1, test2));
int i = 0;
foreach(PropertyDescriptor item in test1)
{
Console.WriteLine(Object.ReferenceEquals(test1[i], test2[i]));
i++;
}
}
- 属性获取程序的MethodInfo也缓存在
中。因此,获取和调用该方法是不可并行的,因为它将对所有线程依赖相同的MethodInfo实例PropertyInfo
(PropertyDescriptor
)在ReflectPropertyDescriptor
期间使用GetValue()
(Type.GetProperty()
)执行的过程,该过程使用属性信息缓存返回结果。RunTimeType.GetProperty()
- 这意味着
和PropertyDescriptor
似乎使用相同的共享缓存返回属性信息实例,这两个实例都用于访问属性getter方法(该方法也被缓存)PropertyInfo
- 这意味着
TL;DR:它一直在缓存中。@mjwills通过vs2017中的并发探查器。我附上了一个截图。@mjwills我有一个问题集,分解成4个部分。每个线程都在不同的线程上处理。它们都返回,结果被分组在一起,并作为一个整体返回。在处理过程中,每个线程对提供的对象使用
PropertyDescriptor
s通过反射检索值。问题是PropertyDescriptor
不是唯一的,因此它们一次只能检索一个值,导致其他线程在检索它们的值之前等待当前线程完成PropertyDescriptor
。GetValue()
是PropertyDescriptor
的一种方法,这是构建在框架中的。类型可以是任何类。当然,它们是相同的,它们应该是(逻辑上)不可变的。这到底是如何导致问题的?最后,如果仔细查看ReflectPropertyDescriptor.GetValue
的代码,它所做的就是使用GetInvocationTarget
获取对象,然后对其使用反射。这是你自己可以做的