对于很长的问题,很抱歉,但是有一个Jon Skeet参考,所以对于某些人来说可能是值得的。
简而言之:在Mono框架中运行时, Interlocked.Read
/ Interlocked.Exchange
似乎比在.NET框架中运行时要慢得多。我很好奇为什么。
很长:
我想要一个用于32位平台的线程安全的double,所以我制作了这个结构:
public interface IThreadSafeDouble
{
double Value { get; set; }
}
public struct LockedThreadSafeDouble : IThreadSafeDouble
{
private readonly object Locker;
private double _Value;
public double Value
{
get { lock (Locker) return _Value; }
set { lock (Locker) _Value = value; }
}
public LockedThreadSafeDouble(object init)
: this()
{
Locker = new object();
}
}
然后,我阅读了乔恩·斯基特(Jon Skeet)对this question的回答,因此我制作了这个结构:
public struct InterlockedThreadSafeDouble : IThreadSafeDouble
{
private long _Value;
public double Value
{
get { return BitConverter.Int64BitsToDouble(Interlocked.Read(ref _Value)); }
set { Interlocked.Exchange(ref _Value, BitConverter.DoubleToInt64Bits(value)); }
}
}
然后我写了这个测试:
private static TimeSpan ThreadSafeDoubleTest2(IThreadSafeDouble dbl)
{
var incrementTarg = 10000000;
var sw = new Stopwatch();
sw.Start();
for (var i = 0; i < incrementTarg; i++, dbl.Value++);
sw.Stop();
return sw.Elapsed;
}
private static void ThreadSafeTest()
{
var interlockedDbl = new InterlockedThreadSafeDouble();
var interlockedTim = ThreadSafeDoubleTest2(interlockedDbl);
var lockedDbl = new LockedThreadSafeDouble(true);
var lockedTim = ThreadSafeDoubleTest2(lockedDbl);
System.Console.WriteLine("Interlocked Time: " + interlockedTim);
System.Console.WriteLine("Locked Time: " + lockedTim);
}
public static void Main(string[] args)
{
for (var i = 0; i < 5; i++)
{
System.Console.WriteLine("Test #" + (i + 1));
ThreadSafeTest();
}
System.Console.WriteLine("Done testing.");
System.Console.ReadLine();
}
我使用.NET框架得到了以下结果:
使用Mono框架的结果如下:
我已经在同一台计算机(Windows XP)上多次运行了这两个测试,结果是一致的。我很好奇,为什么Interlocked.Read/Interlocked.Exchange在Mono框架上的执行速度如此之慢。
更新:
我编写了以下更简单的测试:
long val = 1;
var sw = new Stopwatch();
sw.Start();
for (var i = 0; i < 100000000; i++) {
Interlocked.Exchange(ref val, 2);
// Interlocked.Read(ref val);
}
sw.Stop();
System.Console.WriteLine("Time: " + sw.Elapsed);
.NET框架始终使用
Exchange
和Read
返回〜2.5 秒。 Mono框架返回〜5.1 秒。 最佳答案
得出性能结论并非易事。在第一个示例中,长双转换可能是重要因素。通过将所有双打更改为多头(并删除转换),这些是我在Windows中的32位Mono上的经历:
Test #1
Interlocked Time: 00:00:01.2548628
Locked Time: 00:00:01.7281594
Test #2
Interlocked Time: 00:00:01.2466018
Locked Time: 00:00:01.7219013
Test #3
Interlocked Time: 00:00:01.2590181
Locked Time: 00:00:01.7443508
Test #4
Interlocked Time: 00:00:01.2575325
Locked Time: 00:00:01.7309012
Test #5
Interlocked Time: 00:00:01.2593490
Locked Time: 00:00:01.7528010
Done testing.
因此,互锁实施不是这里的最大因素。
但是,您有第二个示例,没有任何转换。为什么会这样?我认为答案是循环展开,在.NET JIT编译器中做得更好。但这只是一个猜测。如果要比较现实生活中的互锁性能,则有(至少)两个选择:
还要注意,上述实现方式唯一的保证就是您不会观察到撕裂。例如,它不能给您(通常需要的)保证,如果两个线程递增值,则总和是正确的(即,它将所有增量都考虑在内)。