目录
需求
对一个数进行循环累加得到结果。
如果数值是0,循环1000000,我们期望的结果是应该等于1000000,但多线程执行累加,如果不进行处理就不能得到我们想要的结果。
处理方案
lock
关键字Monitor
类SpinLock
自旋锁Semaphore
或SemaphoreSlim
信号量Interlocked
原子操作
先来看执行结果:
可以看到除了第一个,其它方案按预期执行了。
实现代码
- 第一个输出的代码,输出结果不是我们想要的
public static void Main()
{
Console.WriteLine("Hello World!\n");
long count = 1000000;
long num = 0;
object obj = new object();
SpinLock spin = new SpinLock();
SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
Stopwatch stopwatch = new Stopwatch();
//并行累加
stopwatch.Start();
num = 0;
Parallel.For(0, count, i =>
{
num++;
});
stopwatch.Stop();
Console.WriteLine($"并行累加\n结果:{ num }, 耗时:{ stopwatch.ElapsedMilliseconds }\n");
}
lock
关键字
//lock
lock (obj)
{
num++;
}
Monitor
类
//Monitor
Monitor.Enter(obj);
try
{
num++;
}
finally
{
Monitor.Exit(obj);
}
SpinLock
自旋锁
//自旋锁 SpinLock
bool getLock = false;
spin.Enter(ref getLock);
try
{
num++;
}
finally
{
if (getLock)
spin.Exit();
}
Semaphore
或SemaphoreSlim
信号量
//使用信号量 SemaphoreSlim
semaphoreSlim.Wait();
num++;
semaphoreSlim.Release();
Interlocked
原子操作
//使用原子操作
Interlocked.Increment(ref num);
- 完整代码
using System.Diagnostics;
namespace boby.Synchronization
{
internal class Program
{
public static void Main()
{
Console.WriteLine("Hello World!\n");
long count = 1000000;
long num = 0;
object obj = new object();
SpinLock spin = new SpinLock();
SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
Stopwatch stopwatch = new Stopwatch();
//并行累加
stopwatch.Start();
num = 0;
Parallel.For(0, count, i =>
{
num++;
});
stopwatch.Stop();
Console.WriteLine($"并行累加\n结果:{ num }, 耗时:{ stopwatch.ElapsedMilliseconds }\n");
//lock
stopwatch.Start();
num = 0;
Parallel.For(0, count, i =>
{
lock (obj)
{
num++;
}
});
stopwatch.Stop();
Console.WriteLine($"lock\n结果:{ num }, 耗时:{ stopwatch.ElapsedMilliseconds }\n");
//Monitor
stopwatch.Start();
num = 0;
Parallel.For(0, count, i =>
{
Monitor.Enter(obj);
try
{
num++;
}
finally
{
Monitor.Exit(obj);
}
});
stopwatch.Stop();
Console.WriteLine($"Monitor\n结果:{ num }, 耗时:{ stopwatch.ElapsedMilliseconds }\n");
//自旋锁 SpinLock
stopwatch.Start();
num = 0;
Parallel.For(0, count, i =>
{
bool getLock = false;
spin.Enter(ref getLock);
try
{
num++;
}
finally
{
if (getLock)
spin.Exit();
}
});
stopwatch.Stop();
Console.WriteLine($"自旋锁\n结果:{ num }, 耗时:{ stopwatch.ElapsedMilliseconds }\n");
//使用信号量 SemaphoreSlim
stopwatch.Start();
num = 0;
Parallel.For(0, count, i =>
{
semaphoreSlim.Wait();
num++;
semaphoreSlim.Release();
});
stopwatch.Stop();
Console.WriteLine($"信号量\n结果:{ num }, 耗时:{ stopwatch.ElapsedMilliseconds }\n");
//使用原子操作
stopwatch.Start();
num = 0;
Parallel.For(0, count, i =>
{
Interlocked.Increment(ref num);
});
stopwatch.Stop();
Console.WriteLine($"原子操作\n结果:{ num }, 耗时:{ stopwatch.ElapsedMilliseconds }\n");
////使用内存屏障
//stopwatch.Start();
//num = 0;
//Parallel.For(0, count, i =>
//{
// Interlocked.MemoryBarrier();
// num++;
// Interlocked.MemoryBarrier();
//});
//stopwatch.Stop();
//Console.WriteLine($"结果:{ num }, 内存屏障耗时:{ stopwatch.ElapsedMilliseconds }");
}
}
}
总结
-
lock
关键字本质是语法糖,它等价于下面的代码:Monitor.Enter(obj); try { //同步的操作 } finally { Monitor.Exit(obj); }
当然
Monitor
类还支持其它功能,如