写代码过程中,难免会用到多线程,不同线程访问同一资源,常常需要同步锁,避免出现脏数据的情况,C#中常用的有lock关键字以及Monitor、Mutex、Semaphore、SemaphoreSlim等类,它们都能起到同步锁的左右,虽然不是文章说Semaphore不是同步锁的作用,但如果把同时访问的线程数量限制为1,可以起到同步锁的作用,SemaphoreSlim则是Semaphore的轻量级版本,既然它们都可实现同步锁目的,究竟应该选择何种方式?
这就需要根据应用场景及性能来决定,Mutex、Semaphore可以实现跨进程同步,lock关键字、Monitor、SemaphoreSlim则适合进程内同步,很多情况下,我们仅需要在进程内同步,虽然这几种方式都能使用,但究竟应该选择何种方式,需要根据它们的性能来确定,它们性能有什么差别呢?我们来个试验说明:
1、构建一个简单的函数Acc,该函数仅为消耗CPU资源:
private int Acc(int times)
{
times += 1;
return times;
}
2、多种同步方式循环多次调用Acc函数,测试消耗时间,以下是lock及Mutex的代码,其他代码都差不多不一一列举。
private void Test(int times)
{
for (int i = 0; i < times; i++)
{
lock(this)
{
Acc(i);
}
}
}
private void Test(Mutex locker, int times)
{
for(int i = 0; i < times; i++)
{
locker.WaitOne();
Acc(i);
locker.ReleaseMutex();
}
}
3、分别调用这些测试函数,传递相同的times参数,计算耗费时间,下面数据是在我机器上times为0xFFFFF的一次输出数据(单位:毫秒):
Mutex | Semaphore | SemaphoreSlim | lock关键字 | Monitor |
---|---|---|---|---|
1328.54 | 1335.39 | 163.66 | 19.92 | 18.93 |
从耗费时间来看,它们之间的性能为量级的差别,Mutex、Semaphore同一级别耗费时间最多、SemaphoreSlim耗费时间次之,lock关键字、Monitor同一一个级别耗费最少,因此我们实际使用过程中,需要根据需要选择同步方式,lock关键字及Monitor类同一级别,但编程便利性不一样,lock最简单,但是lock无法设定超时,有些情况下可能导致死锁,Monitor则可设定超时,代码写不好异常时可能导致没有Release,这需要我们根据情况选定。