Thread.MemoryBarrier和一个简单属性的锁定差异

对于以下场景,使用MemoryBarrier之间的线程安全性,结果和性能是否有任何区别

 private SomeType field; public SomeType Property { get { Thread.MemoryBarrier(); SomeType result = field; Thread.MemoryBarrier(); return result; } set { Thread.MemoryBarrier(); field = value; Thread.MemoryBarrier(); } } 

lock语句( Monitor.EnterMonitor.Exit

 private SomeType field; private readonly object syncLock = new object(); public SomeType Property { get { lock (syncLock) { return field; } } set { lock (syncLock) { field = value; } } } 

因为引用赋值是primefaces的所以我认为在这种情况下我们确实需要任何锁定机制。

性能 MemeoryBarrier比Release的锁实现快约2倍。 以下是我的测试结果:

 Lock Normaly: 5397 ms Passed as interface: 5431 ms Double Barrier Normaly: 2786 ms Passed as interface: 3754 ms volatile Normaly: 250 ms Passed as interface: 668 ms Volatile Read/Write Normaly: 253 ms Passed as interface: 697 ms ReaderWriterLockSlim Normaly: 9272 ms Passed as interface: 10040 ms Single Barrier: freshness of Property Normaly: 1491 ms Passed as interface: 2510 ms Single Barrier: other not reodering Normaly: 1477 ms Passed as interface: 2275 ms 

以下是我在LINQPad中测试它的方法(在Preferences中设置了优化):

 void Main() { "Lock".Dump(); string temp; var a = new A(); var watch = Stopwatch.StartNew(); for (int i = 0; i < 100000000; ++i) { temp = a.Property; a.Property = temp; } Console.WriteLine("Normaly: " + watch.ElapsedMilliseconds + " ms"); Test(a); "Double Barrier".Dump(); var b = new B(); watch.Restart(); for (int i = 0; i < 100000000; ++i) { temp = b.Property; b.Property = temp; } Console.WriteLine("Normaly: " + watch.ElapsedMilliseconds + " ms"); Test(b); "volatile".Dump(); var c = new C(); watch.Restart(); for (int i = 0; i < 100000000; ++i) { temp = c.Property; c.Property = temp; } Console.WriteLine("Normaly: " + watch.ElapsedMilliseconds + " ms"); Test(c); "Volatile Read/Write".Dump(); var d = new D(); watch.Restart(); for (int i = 0; i < 100000000; ++i) { temp = d.Property; d.Property = temp; } Console.WriteLine("Normaly: " + watch.ElapsedMilliseconds + " ms"); Test(d); "ReaderWriterLockSlim".Dump(); var e = new E(); watch.Restart(); for (int i = 0; i < 100000000; ++i) { temp = e.Property; e.Property = temp; } Console.WriteLine("Normaly: " + watch.ElapsedMilliseconds + " ms"); Test(e); "Single Barrier: freshness of Property".Dump(); var f = new F(); watch.Restart(); for (int i = 0; i < 100000000; ++i) { temp = f.Property; f.Property = temp; } Console.WriteLine("Normaly: " + watch.ElapsedMilliseconds + " ms"); Test(f); "Single Barrier: other not reodering".Dump(); var g = new G(); watch.Restart(); for (int i = 0; i < 100000000; ++i) { temp = g.Property; g.Property = temp; } Console.WriteLine("Normaly: " + watch.ElapsedMilliseconds + " ms"); Test(g); } void Test(I a) { string temp; var watch = Stopwatch.StartNew(); for (int i = 0; i < 100000000; ++i) { temp = a.Property; a.Property = temp; } Console.WriteLine("Passed as interface: " + watch.ElapsedMilliseconds + " ms\n"); } interface I { string Property { get; set; } } class A : I { private string field; private readonly object syncLock = new object(); public string Property { get { lock (syncLock) { return field; } } set { lock (syncLock) { field = value; } } } } class B : I { private string field; public string Property { get { Thread.MemoryBarrier(); string result = field; Thread.MemoryBarrier(); return result; } set { Thread.MemoryBarrier(); field = value; Thread.MemoryBarrier(); } } } class C : I { private volatile string field; public string Property { get { return field; } set { field = value; } } } class D : I { private string field; public string Property { get { return Volatile.Read(ref field); } set { Volatile.Write(ref field, value); } } } class E : I { private string field; private ReaderWriterLockSlim locker = new ReaderWriterLockSlim(); public string Property { get { locker.EnterReadLock(); string result = field; locker.ExitReadLock(); return result; } set { locker.EnterReadLock(); field = value; locker.ExitReadLock(); } } } class F : I { private string field; public string Property { get { Thread.MemoryBarrier(); return field; } set { field = value; Thread.MemoryBarrier(); } } } class G : I { private string field; public string Property { get { string result = field; Thread.MemoryBarrier(); return result; } set { Thread.MemoryBarrier(); field = value; } } } 

线程安全性有什么不同吗?

两者都确保围绕读写设置适当的障碍。

结果?

在这两种情况下,两个线程都可以竞争写入值。 但是,读取和写入不能在锁定或完全围栏之后向前或向后移动。

性能?

你已经两种方式编写代码。 现在运行它 。 如果你想知道哪个更快,运行它并找出答案! 如果你有两匹马并且你想知道哪匹马更快,那么就比赛吧。 不要在网上问陌生人他们认为哪匹马更快。

也就是说,更好的技术是设定一个性能目标 ,编写代码以使其清晰正确,然后测试以确定您是否达到了目标。 如果你这样做了,不要浪费宝贵的时间来尝试优化已经足够快的代码; 花钱去优化其他不够快的东西。

你没问的一个问题:

你会怎么做?

我不会写一个multithreading程序,这就是我要做的。 如果必须的话,我会使用进程作为我的并发单元。

如果我必须编写multithreading程序,那么我将使用最高级别的工具。 我使用任务并行库,我使用async-await,我使用Lazy等等。 我会避免共享记忆; 我将线程视为以异步方式返回值的轻量级进程。

如果我必须编写共享内存multithreading程序,那么我会一直锁定所有内容 。 这些天我们经常编写程序,通过卫星链接获取十亿字节的video并将其发送到手机。 花在锁上20纳秒并不会杀了你。

我不够聪明,试图编写低锁代码,所以我根本不会这样做。 如果我不得不那么我将使用低锁代码来构建更高级别的抽象并使用该抽象。 幸运的是,我没有必要,因为有人已经建立了我需要的抽象。

只要有问题的变量是可以primefaces获取/设置的有限变量集(即引用类型)之一,那么是的,这两个解决方案正在应用相同的线程相关约束。

也就是说,老实说,我希望MemoryBarrier解决方案比锁定更糟糕 。 访问无争议的lock非常快。 它已针对该情况进行了专门优化。 另一方面,引入内存屏障,不仅影响对一个变量的访问,如lock的情况,而且影响所有内存 ,在应用程序的其他方面可能非常容易产生显着的负面性能影响。 您当然需要进行一些测试以确定(对于您的实际应用程序,因为单独测试这两个应用程序并不会揭示内存屏障迫使应用程序内存的所有其余内容同步的事实,而不仅仅是这一个变量)。

线程安全性没有区别。 但是,我更喜欢:

 private SomeType field public SomeType Property { get { return Volatile.Read(ref field); } set { Volatile.Write(ref field, value); } } 

要么,

 private volatile SomeType field public SomeType Property { get { return field; } set { field = value; } }