对于以下情况,使用 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.Enter
和Monitor.Exit
)
private SomeType field;
private readonly object syncLock = new object();
public SomeType Property
{
get
{
lock (syncLock)
{
return field;
}
}
set
{
lock (syncLock)
{
field = value;
}
}
}
因为引用赋值是原子的,所以在这种情况下我们不需要任何锁定机制。
性能 对于发布版本,MemoryBarrier比锁实现快约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中进行测试的方法(在首选项中设置了优化):
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;
}
}
}