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也缓存在
    PropertyInfo
    中。因此,获取和调用该方法是不可并行的,因为它将对所有线程依赖相同的MethodInfo实例
  • PropertyDescriptor
    ReflectPropertyDescriptor
    )在
    GetValue()
    期间使用
    Type.GetProperty()
    RunTimeType.GetProperty()
    )执行的过程,该过程使用属性信息缓存返回结果。
    • 这意味着
      PropertyDescriptor
      PropertyInfo
      似乎使用相同的共享缓存返回属性信息实例,这两个实例都用于访问属性getter方法(该方法也被缓存)

TL;DR:它一直在缓存中。

@mjwills通过vs2017中的并发探查器。我附上了一个截图。@mjwills我有一个问题集,分解成4个部分。每个线程都在不同的线程上处理。它们都返回,结果被分组在一起,并作为一个整体返回。在处理过程中,每个线程对提供的对象使用
PropertyDescriptor
s通过反射检索值。问题是
PropertyDescriptor
不是唯一的,因此它们一次只能检索一个值,导致其他线程在检索它们的值之前等待当前线程完成
PropertyDescriptor
GetValue()
PropertyDescriptor
的一种方法,这是构建在框架中的。类型可以是任何类。当然,它们是相同的,它们应该是(逻辑上)不可变的。这到底是如何导致问题的?最后,如果仔细查看
ReflectPropertyDescriptor.GetValue
的代码,它所做的就是使用
GetInvocationTarget
获取对象,然后对其使用反射。这是你自己可以做的