多线程下解决资源竞争的7种方法

前言一般情况下,只要涉及到多线程编程,程序的复杂性就会显著上升,性能显著下降,BUG出现的概率大大提升。多线程编程本意是将一段程序并行运行,提升数据处理能力,但是由于大部分情况下都涉及到共有资源的竞争,所以修改资源对象时必须加锁处理。但是锁的实现有很多种方法,下面就来一起了解一下在C#语言中几种锁的实现与其性能表现。一、c#下的几种锁的运用方式1、临界区,通过对多线程的串行化来访问公共资源或一段代...

多线程下解决资源竞争的7种方法

前言

  一般情况下,只要涉及到多线程编程,程序的复杂性就会显著上升,性能显著下降,BUG出现的概率大大提升。

多线程编程本意是将一段程序并行运行,提升数据处理能力,但是由于大部分情况下都涉及到共有资源的竞争,所以修改资源

对象时必须加锁处理。但是锁的实现有很多种方法,下面就来一起了解一下在C#语言中几种锁的实现与其性能表现。

一、c#下的几种锁的运用方式

1、临界区,通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。

 1private static object obj = new object(); 2private static int lockInt; 3private static void LockIntAdd() 4   { 5 for (var i = 0; i < runTimes; i  ) 6 { 7  lock (obj) 8  { 9lockInt  ;10  }11 }12}

你没看错,c#中的lock语法就是临界区(Monitor)的一个语法糖,这大概是90%以上的.net程序员首先想到的锁,不过大部分人都只是知道

有这么个语法,不知道其实是以临界区的方式处理资源竞争。

2、互斥量,为协调共同对一个共享资源的单独访问而设计的。

c#中有一个Mutex类,就在System.Threading命名空间下,Mutex其实就是互斥量,互斥量不单单能处理多线程之间的资源竞争,还能处理

进程之间的资源竞争,功能是比较强大的,但是开销也很大,性能比较低。

 1private static Mutex mutex = new Mutex(); 2private static int mutexInt; 3private static void MutexIntAdd() 4   { 5 for (var i = 0; i < runTimes; i  ) 6 { 7  mutex.WaitOne(); 8  mutexInt  ; 9  mutex.ReleaseMutex();10 }11}

3、信号量,为控制一个具有有限数量用户资源而设计。

 1private static Semaphore sema = new Semaphore(1, 1); 2private static int semaphoreInt; 3private static void SemaphoreIntAdd() 4   { 5 for (var i = 0; i < runTimes; i  ) 6 { 7  sema.WaitOne(); 8  semaphoreInt  ; 9  sema.Release();10 }11}

4、事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始。

 1public static AutoResetEvent autoResetEvent = new AutoResetEvent(true); 2private static int autoResetEventInt; 3private static void AutoResetEventIntAdd() 4   { 5 for (var i = 0; i < runTimes; i  ) 6 { 7  if (autoResetEvent.WaitOne()) 8  { 9autoResetEventInt  ;10   autoResetEvent.Set();11  }12 }13}

5、读写锁,这种锁允许在有其他程序正在写的情况下读取资源,所以如果资源允许脏读,用这个比较合适

 1private static ReaderWriterLockSlim LockSlim = new ReaderWriterLockSlim(); 2private static int lockSlimInt; 3private static void LockSlimIntAdd() 4   { 5 for (var i = 0; i < runTimes; i  ) 6 { 7  LockSlim.EnterWriteLock(); 8  lockSlimInt  ; 9  LockSlim.ExitWriteLock();10 }11}

6、原子锁,通过原子操作Interlocked.CompareExchange实现“无锁”竞争

 1private static int isLock; 2private static int ceInt; 3private static void CEIntAdd() 4   { 5 //long tmp = 0; 6 for (var i = 0; i < runTimes; i  ) 7 { 8  while (Interlocked.CompareExchange(ref isLock, 1, 0) == 1) { Thread.Sleep(1); } 9 10  ceInt  ;11  Interlocked.Exchange(ref isLock, 0);12 }13}

7、原子性操作,这是一种特例,野外原子性操作本身天生线程安全,所以无需加锁

1private static int atomicInt;2private static void AtomicIntAdd()3   {4 for (var i = 0; i < runTimes; i  )5 {6  Interlocked.Increment(ref atomicInt);7 }8}

8、不加锁,如果不加锁,那多线程下运行结果肯定是错的,这里贴上来比较一下性能

1private static int noLockInt;2private static void NoLockIntAdd()3   {4 for (var i = 0; i < runTimes; i  )5 {6  noLockInt  ;7 }8}

二、性能测试

1、测试代码,执行1000,10000,100000,1000000次

https://www.guoxiongfei.cn/cntech/24330.html