ThreadSaveCounter.txt ---------- 20191106 https://dotblogs.com.tw/pinsblog/2018/02/07/102741 C# 幾種不同 thread-safe counter 的作法效能比較 看到專案裡面之前用 SemaphoreSlim 來計數,覺得還要 dispose 有點麻煩,就 google 看看其他人的做法,並寫來比較看看效能 這裡比較了三種做法的時間,用 Unit test 跑的時間來參考,三種分別如下 內建的 Interlocked.Increment、Interlocked.Decrement 將 ++ 與 -- 用 lock 陳述式包起來 用 SemaphoreSlim.Release 與 Semaphore.Wait 第一種、 Interlocked.Increment、Interlocked.Decrement [TestMethod] public void InterlockedTest() { var count = 0; var taskList = new List(); for (int i = 0; i < 4; i++) { taskList.Add(Task.Run(() => { for (int j = 0; j < 1_000_000; j++) { Interlocked.Increment(ref count); Interlocked.Decrement(ref count); } })); } Task.WaitAll(taskList.ToArray()); Assert.AreEqual(0, count); } 第二種、將 ++ 與 -- 用 lock 陳述式包起來 [TestMethod] public void LockTest() { var myLock = new object(); var count = 0; var taskList = new List(); for (int i = 0; i < 4; i++) { taskList.Add(Task.Run(() => { for (int j = 0; j < 1_000_000; j++) { lock (myLock) { count++; } lock (myLock) { count--; } } })); } Task.WaitAll(taskList.ToArray()); Assert.AreEqual(0, count); } 第三種、用 SemaphoreSlim.Release 與 Semaphore.Wait [TestMethod] public void SemaphoreSlimTest() { var count = new SemaphoreSlim(0, int.MaxValue); var taskList = new List(); for (int i = 0; i < 4; i++) { taskList.Add(Task.Run(() => { for (int j = 0; j < 1_000_000; j++) { count.Release(); count.Wait(); } })); } Task.WaitAll(taskList.ToArray()); Assert.AreEqual(0, count.CurrentCount); } 從測試結果看起來,Interlocked 是最快的,但不同的方法有不同的好處,Interlocked 的效能是最高的,如果有需求頻繁的加加減減,可以用 Interlocked。Lock 的好處是可以連其他希望 Atomic 一起寫進去。SemaphoreSlim 則是做其他事情比較好用,因為有支援 async Wait,SemaphoreSlim 本身不太適合拿來當 Counter,因為只能一定要先加再減,用處比較受限。