.Net基于Thread實現自旋鎖的三種方式

 更新時間:2021年06月16日 10:04:25   作者:初夏的陽光丶  
本文主要講解.Net基于Thread實現自旋鎖的三種方式,基于Test--And--Set原子操作實現,包含優缺點介紹,感興趣的朋友跟隨小編一起看看吧


基于Thread.SpinWait實現自旋鎖

實現原理:基于Test--And--Set原子操作實現
使用一個數據表示當前鎖是否已經被獲取 0表示未被索取,1表示已經獲取 獲取鎖時會將_lock的值設置為1 然后檢查修改前的值是否等于0,

優點:

  • 不使用Thread.SpinWait方法,重試的方法體會為空,CPU會使用它的最大性能來不斷的進行賦值和比較指令,會浪費很大的性能,Thread.SpinWait提示CPU當前正在自旋鎖的循環中,可以休息若干個時間周期
  • 使用自旋鎖需要注意的問題,自旋鎖保護的代碼應該在非常短的時間內執行完成,如果時間過長,其他線程不斷重試導致影響其他線程進行

缺點:

當前實現沒有考慮到公平性,如果多個線程同時獲取鎖失敗,按時間順序第一個獲取鎖的線程不一定會在釋放鎖后第一個獲取成功,

代碼實現:

public static class ThreadSpinWaitDemo
    {
        private static int _lock = 0;
        private static int _counterA = 0;
        private static int _counterB = 0;

        public static void IncrementCounters()
        {
            while (Interlocked.Exchange(ref _lock, 1) != 0)
            {
                Thread.SpinWait(1);
            }

            ++_counterA;
            ++_counterB;
            Interlocked.Exchange(ref _lock, 0);
        }

        public static void GetCounters(out int counterA, out int counterB)
        {
            while (Interlocked.Exchange(ref _lock, 1) != 0)
            {
                Thread.SpinWait(1);
            }
            counterA = _counterA;
            counterB = _counterB;
            Interlocked.Exchange(ref _lock, 0);

        }
    }

基于SpinWaite實現自旋鎖

特性是SpinOnce方法的次數,如果在一定次數以內并且當前邏輯核心所大于1,則調用Thread.SpinWait函數;如果超過一定次數或者當前環境邏輯核心數等于1,則交替使用
Thread.Sleep(0)和Thread.Yield函數,表示切換到其他線程,如果再超過一定次數,則讓當前線程休眠
SpinWaite解決Thread.SpinWait中的兩個問題

  • 如果自旋鎖運行時間超長,SpinWaite可以提示操作系統切換到其他線程或者讓當前線程進入休眠狀態,
  • 如果當前環境只有一個核心邏輯,SpinWaite不會執行Thread.SpinWait函數,而是直接提示操作系統切換到其他線程,
public static class ThreadSpinOnceDemo
    {
        private static int _lock = 0;
        private static int _counterA = 0;
        private static int _counterB = 0;


        public static void IncrementCounters()
        {
            var spinWait = new SpinWait();
            while (Interlocked.Exchange(ref _lock, 1) != 0)
            {
                spinWait.SpinOnce();
            }

            ++_counterA;
            ++_counterB;
            Interlocked.Exchange(ref _lock, 0);
        }

        public static void GetCounters(out int counterA, out int counterB)
        {
            var spinWait = new SpinWait();
            while (Interlocked.Exchange(ref _lock, 1) != 0)
            {
                spinWait.SpinOnce();
            }
            counterA = _counterA;
            counterB = _counterB;
            Interlocked.Exchange(ref _lock, 0);

        }
    }

基于SpinLock實現自旋鎖

封裝了SpinWaite的邏輯

SpinLock代碼實現

public class ThreadSpinLockDemo
    {
        private static SpinLock _spinLock = new SpinLock();
        private static int _counterA = 0;
        private static int _counterB = 0;

        public static void IncrementCounters()
        {
            bool lockTaken = false;
            try
            {
                _spinLock.Enter(ref lockTaken);
                ++_counterA;
                ++_counterB;
            }
            finally
            {
                if (lockTaken)
                {
                    _spinLock.Exit();
                }
            }
        }

        public static void GetCounters(out int counterA, out int counterB)
        {
            bool lockTaken = false;
            try
            {
                _spinLock.Enter(ref lockTaken);
                counterA = _counterA;
                counterB = _counterB;
            }
            finally
            {
                if (lockTaken)
                {
                    _spinLock.Exit();
                }
            }
        }
    }

簡述 Thread.Sleep(0)和Thread.Yield的區別

  • 在Windows系統中 Thread.Sleep調用系統提供的SleepEx函數,Thread.Yield函數調用的是系統提供的SwitchToThread方法,
  • 區別在于SwitchToThread函數只會切換到當前核心邏輯關聯的待運行隊列的線程,不會切換到其他核心邏輯關聯的線程上,而SleepEx函數會切換到任意邏輯核心關聯的待運行隊列中的線程,并且讓當前線程在指定時間內無法重新進入待運行隊列(如果線程為0 那么線程可以立刻重新進入待運行隊列)
  • 在Linux和OSX中 Thread.Sleep函數在休眠時間不為0時會調用pthread類庫提供的pthread_cond_timedWait函數,在休眠時間為0時會調用sched_yield函數,Thread.Yield同樣會調用sched_yield函數 sched_yield在windows和osx系統中沒有區別,都只會切換到當前和邏輯核心關心的待運行隊列中的線程,不會切換到其他核心邏輯關聯的線程上。在unix系統上調用系統提供的sleep函數并傳入0 會直接忽略返回

以上就是.Net基于Thread實現自旋鎖的三種方式的詳細內容,更多關于.Net自旋鎖的資料請關注腳本之家其它相關文章!

相關文章

最新評論

精品国内自产拍在线观看