通过反射以原子方式读取字段的值

时间:2015-12-17 12:57:53

标签: c# reflection atomic

假设我有以下C#声明:

struct Counters
{
    public long a;
    public long b;
    public long c;
}

是否可以遍历给定Counters实例的字段并使用Interlocked.Read()读取其值?即。

Counters counters;
foreach (var counter in typeof(Counters).GetFields())
{
    var value = Interlocked.Read(???);
}

3 个答案:

答案 0 :(得分:1)

您无法直接使用Interlocked.Read,因为它需要ref参数 - 而且您无法直接使用所需的System.Int64&类型。

所以,回到反思:

// You can keep this static in some helper class
var method = typeof(Interlocked).GetMethod("Read", new []{ typeof(long).MakeByRefType() });

var result = (long)method.Invoke(null, new object[] { counter.GetValue(instance) });

<击>

编辑:这也不起作用,我搞砸了我的测试。你还在阅读一本原封不动的副本。

但这会奏效:

public delegate long AtomicReadDelegate<T>(ref T instance);

public static AtomicReadDelegate<T> AtomicRead<T>(string name)
{
  var dm = new DynamicMethod(typeof(T).Name + "``" + name + "``AtomicRead", typeof(long), 
                             new [] { typeof(T).MakeByRefType() }, true);

  var il = dm.GetILGenerator();

  il.Emit(OpCodes.Ldarg_0);
  il.Emit(OpCodes.Ldflda, typeof(T).GetField(name));
  il.Emit(OpCodes.Call, 
     typeof(Interlocked).GetMethod("Read", new [] { typeof(long).MakeByRefType() }));

  il.Emit(OpCodes.Ret);

  return (AtomicReadDelegate<T>)dm.CreateDelegate(typeof(AtomicReadDelegate<T>));
}

private readonly AtomicReadDelegate<Counters>[] _allTheReads = 
  new []
  {
    AtomicRead<Counters>("a"),
    AtomicRead<Counters>("b"),
    AtomicRead<Counters>("c")
  };

public static void SomeTest(ref Counters counters)
{
  foreach (var fieldRead in _allTheReads)
  {
    var value = fieldRead(ref counters);

    Console.WriteLine(value);
  }
}

您可能希望缓存从AtomicRead获得的代理 - 它们可以安全地重复使用。一个简单的并发字典就可以了。

别忘了这只支持long;如果你还需要原子地读取其他类型,你需要使用Interlocked.CompareExchange(当然除了引用和int之外 - 虽然取决于你的代码,你可能需要一些内存障碍即使在那种情况下)。

答案 1 :(得分:0)

实例字段的值是每个对象,因此您需要获取特定对象的值。

Counters counter1 = new Counter() { a = 40; b = 50; c = 60; }
Type counterType = counter1.GetType();
foreach (var field in counterType.GetFields())
{
    var value = Interlocked.Read(field.GetValue(counter1));
}

在这种情况下,我们获取counter1字段的值,而不是任何其他结构实例。

答案 2 :(得分:0)

如果你真的需要一个原子序,那么最好使用atomics.net library(可以通过NuGet获得)。 如果你只需要读取你的线程中传递的struct的值,那么它是安全的,因为它是通过值传递的。但如果它是通过引用传递的,或者如果你使用不安全/本机代码,那么最好说出你想要实现的目标。