亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C#多線程編程中的鎖系統(四):自旋鎖

2020-01-24 02:00:42
字體:
來源:轉載
供稿:網友

目錄
一:基礎

二:自旋鎖示例

三:SpinLock

四:繼續SpinLock

五:總結

一:基礎

內核鎖:基于內核對象構造的鎖機制,就是通常說的內核構造模式。用戶模式構造和內核模式構造

           優點:cpu利用最大化。它發現資源被鎖住,請求就排隊等候。線程切換到別處干活,直到接受到可用信號,線程再切回來繼續處理請求。

           缺點:托管代碼->用戶模式代碼->內核代碼損耗、線程上下文切換損耗。

                   在鎖的時間比較短時,系統頻繁忙于休眠、切換,是個很大的性能損耗。

自旋鎖:原子操作+自循環。通常說的用戶構造模式。  線程不休眠,一直循環嘗試對資源訪問,直到可用。

           優點:完美解決內核鎖的缺點。

           缺點:長時間一直循環會導致cpu的白白浪費,高并發競爭下、CPU的消耗特別嚴重。

混合鎖:內核鎖+自旋鎖。 混合鎖是先自旋鎖一段時間或自旋多少次,再轉成內核鎖。

           優點:內核鎖和自旋鎖的折中方案,利用前二者優點,避免出現極端情況(自旋時間過長,內核鎖時間過短)。

           缺點: 自旋多少時間、自旋多少次,這些策略很難把控。

           ps:操作系統或net框架,這塊算法策略做的已經非常優了,有些API函數也提供了時間及次數可配置項,讓開發者根據需求自行判斷。

 

二:自旋鎖示例

來看下我們自己簡單實現的自旋鎖:

復制代碼 代碼如下:

int signal = 0;
            var li = new List<int>();
            Parallel.For(0, 1000 * 10000, r =>
            {
                while (Interlocked.Exchange(ref signal, 1) != 0)//加自旋鎖
                {
                    //黑魔法
                }
                li.Add(r);
                Interlocked.Exchange(ref signal, 0);  //釋放鎖
            });
            Console.WriteLine(li.Count);
            //輸出:10000000

上面就是自旋鎖:Interlocked.Exchange+while

1:定義signal  0可用,1不可用。

2:Parallel模擬并發競爭,原子更改signal狀態。 后續線程自旋訪問signal,是否可用。

3:A線程使用完后,更改signal為0。 剩余線程競爭訪問資源,B線程勝利后,更改signal為1,失敗線程繼續自旋,直到可用。

三:SpinLock

SpinLock是net4.0后系統幫我們實現的自旋鎖,內部做了優化。

簡單看下實例:
 

復制代碼 代碼如下:

 var li = new List<int>();
            var sl = new SpinLock();
            Parallel.For(0, 1000 * 10000, r =>
            {
                bool gotLock = false;     //釋放成功
                sl.Enter(ref gotLock);    //進入鎖
                li.Add(r);
                if (gotLock) sl.Exit();  //釋放
            });
            Console.WriteLine(li.Count);
            //輸出:10000000
 

四:繼續SpinLock

new SpinLock(false)   這個構造函數主要用來幫我們檢查死鎖用,true是開啟。

開啟狀態下,如果發生死鎖會直接拋異常的。

貼了一部分源碼(已折疊),我們來看下:

復制代碼 代碼如下:

public void Enter(ref bool lockTaken)
        {
            if (lockTaken)
            {
                lockTaken = false;
                throw new System.ArgumentException(Environment.GetResourceString("SpinLock_TryReliableEnter_ArgumentException"));
            }

            // Fast path to acquire the lock if the lock is released
            // If the thread tracking enabled set the new owner to the current thread id
            // Id not, set the anonymous bit lock
            int observedOwner = m_owner;
            int newOwner = 0;
            bool threadTrackingEnabled = (m_owner & LOCK_ID_DISABLE_MASK) == 0;
            if (threadTrackingEnabled)
            {
                if (observedOwner == LOCK_UNOWNED)
                    newOwner = Thread.CurrentThread.ManagedThreadId;
            }
            else if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
            {
                newOwner = observedOwner | LOCK_ANONYMOUS_OWNED; // set the lock bit
            }
            if (newOwner != 0)
            {
#if !FEATURE_CORECLR
                Thread.BeginCriticalRegion();
#endif

#if PFX_LEGACY_3_5
                if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner) == observedOwner)
                {
                    lockTaken = true;
                    return;
                }
#else
                if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner)
                {
                    // Fast path succeeded
                    return;
                }
#endif
#if !FEATURE_CORECLR
                Thread.EndCriticalRegion();
#endif
            }
            //Fast path failed, try slow path
            ContinueTryEnter(Timeout.Infinite, ref lockTaken);
        }
private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken)
        {
            long startTicks = 0;
            if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout != 0)
            {
                startTicks = DateTime.UtcNow.Ticks;
            }

#if !FEATURE_PAL && !FEATURE_CORECLR   // PAL doesn't support  eventing, and we don't compile CDS providers for Coreclr
            if (CdsSyncEtwBCLProvider.Log.IsEnabled())
            {
                CdsSyncEtwBCLProvider.Log.SpinLock_FastPathFailed(m_owner);
            }
#endif

            if (IsThreadOwnerTrackingEnabled)
            {
                // Slow path for enabled thread tracking mode
                ContinueTryEnterWithThreadTracking(millisecondsTimeout, startTicks, ref lockTaken);
                return;
            }

            // then thread tracking is disabled
            // In this case there are three ways to acquire the lock
            // 1- the first way the thread either tries to get the lock if it's free or updates the waiters, if the turn >= the processors count then go to 3 else go to 2
            // 2- In this step the waiter threads spins and tries to acquire the lock, the number of spin iterations and spin count is dependent on the thread turn
            // the late the thread arrives the more it spins and less frequent it check the lock avilability
            // Also the spins count is increaes each iteration
            // If the spins iterations finished and failed to acquire the lock, go to step 3
            // 3- This is the yielding step, there are two ways of yielding Thread.Yield and Sleep(1)
            // If the timeout is expired in after step 1, we need to decrement the waiters count before returning
 
            int observedOwner;

            //***Step 1, take the lock or update the waiters
 
            // try to acquire the lock directly if possoble or update the waiters count
            SpinWait spinner = new SpinWait();
            while (true)
            {
                observedOwner = m_owner;
                if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
                {
#if !FEATURE_CORECLR
                    Thread.BeginCriticalRegion();
#endif
 
#if PFX_LEGACY_3_5
                    if (Interlocked.CompareExchange(ref m_owner, observedOwner | 1, observedOwner) == observedOwner)
                    {
                        lockTaken = true;
                        return;
                    }
#else
                    if (Interlocked.CompareExchange(ref m_owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner)
                    {
                        return;
                    }
#endif

#if !FEATURE_CORECLR
                    Thread.EndCriticalRegion();
#endif
                }
                else //failed to acquire the lock,then try to update the waiters. If the waiters count reached the maximum, jsut break the loop to avoid overflow
                    if ((observedOwner & WAITERS_MASK) ==  MAXIMUM_WAITERS || Interlocked.CompareExchange(ref m_owner, observedOwner + 2, observedOwner) == observedOwner)
                        break;
 
                spinner.SpinOnce();
            }

            // Check the timeout.
            if (millisecondsTimeout == 0 ||
                (millisecondsTimeout != Timeout.Infinite &&
                TimeoutExpired(startTicks, millisecondsTimeout)))
            {
                DecrementWaiters();
                return;
            }

            //***Step 2. Spinning
            //lock acquired failed and waiters updated
            int turn = ((observedOwner + 2) & WAITERS_MASK) / 2;
            int processorCount = PlatformHelper.ProcessorCount;
            if (turn < processorCount)
            {
                int processFactor = 1;
                for (int i = 1; i <= turn * SPINNING_FACTOR; i++)
                {
                    Thread.SpinWait((turn + i) * SPINNING_FACTOR * processFactor);
                    if (processFactor < processorCount)
                        processFactor++;
                    observedOwner = m_owner;
                    if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
                    {
#if !FEATURE_CORECLR
                        Thread.BeginCriticalRegion();
#endif
 
                        int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero
                            observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters
                            : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit
                        Contract.Assert((newOwner & WAITERS_MASK) >= 0);
#if PFX_LEGACY_3_5
                        if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner) == observedOwner)
                        {
                            lockTaken = true;
                            return;
                        }
#else
                        if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner)
                        {
                            return;
                        }
#endif

#if !FEATURE_CORECLR
                        Thread.EndCriticalRegion();
#endif
                    }
                }
            }

            // Check the timeout.
            if (millisecondsTimeout != Timeout.Infinite && TimeoutExpired(startTicks, millisecondsTimeout))
            {
                DecrementWaiters();
                return;
            }

            //*** Step 3, Yielding
            //Sleep(1) every 50 yields
            int yieldsoFar = 0;
            while (true)
            {
                observedOwner = m_owner;
                if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
                {
#if !FEATURE_CORECLR
                    Thread.BeginCriticalRegion();
#endif
                    int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero
                           observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters
                           : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit
                    Contract.Assert((newOwner & WAITERS_MASK) >= 0);
#if PFX_LEGACY_3_5
                    if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner) == observedOwner)
                    {
                        lockTaken = true;
                        return;
                    }
#else
                    if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner)
                    {
                        return;
                    }
#endif
 
#if !FEATURE_CORECLR
                    Thread.EndCriticalRegion();
#endif
                }

                if (yieldsoFar % SLEEP_ONE_FREQUENCY == 0)
                {
                    Thread.Sleep(1);
                }
                else if (yieldsoFar % SLEEP_ZERO_FREQUENCY == 0)
                {
                    Thread.Sleep(0);
                }
                else
                {
#if PFX_LEGACY_3_5
                    Platform.Yield();
#else
                    Thread.Yield();
#endif
                }
 
                if (yieldsoFar % TIMEOUT_CHECK_FREQUENCY == 0)
                {
                    //Check the timeout.
                    if (millisecondsTimeout != Timeout.Infinite && TimeoutExpired(startTicks, millisecondsTimeout))
                    {
                        DecrementWaiters();
                        return;
                    }
                }

                yieldsoFar++;
            }
        }
 
        /// <summary>
        /// decrements the waiters, in case of the timeout is expired
        /// </summary>
        private void DecrementWaiters()
        {
            SpinWait spinner = new SpinWait();
            while (true)
            {
                int observedOwner = m_owner;
                if ((observedOwner & WAITERS_MASK) == 0) return; // don't decrement the waiters if it's corrupted by previous call of Exit(false)
                if (Interlocked.CompareExchange(ref m_owner, observedOwner - 2, observedOwner) == observedOwner)
                {
                    Contract.Assert(!IsThreadOwnerTrackingEnabled); // Make sure the waiters never be negative which will cause the thread tracking bit to be flipped
                    break;
                }
                spinner.SpinOnce();
            }
 
        }

從代碼中發現SpinLock并不是我們簡單的實現那樣一直自旋,其內部做了很多優化。 

1:內部使用了Interlocked.CompareExchange保持原子操作, m_owner 0可用,1不可用。

2:第一次獲得鎖失敗后,繼續調用ContinueTryEnter,ContinueTryEnter有三種獲得鎖的情況。

3:ContinueTryEnter函數第一種獲得鎖的方式。 使用了while+SpinWait,后續再講。

4:第一種方式達到最大等待者數量后,命中走第二種。 繼續自旋 turn * 100次。100這個值是處理器核數(4, 8 ,16)下最好的。

5:第二種如果還不能獲得鎖,走第三種。   這種就有點混合構造的意味了,如下:

復制代碼 代碼如下:

if (yieldsoFar % 40 == 0)
                    Thread.Sleep(1);
                else if (yieldsoFar % 10 == 0)
                    Thread.Sleep(0);
                else
                    Thread.Yield();

Thread.Sleep(1) : 終止當前線程,放棄剩下時間片 休眠1毫秒。 退出跟其他線程搶占cpu。當然這個一般會更多,系統無法保證這么細的時間粒度。

Thread.Sleep(0):  終止當前線程,放棄剩下時間片。  但立馬還會跟其他線程搶cpu,能不能搶到跟線程優先級有關。

Thread.Yeild():       結束當前線程。讓出cpu給其他準備好的線程。其他線程ok后或沒有準備好的線程,繼續執行。 跟優先級無關。

Thread.Yeild()還會返回個bool值,是否讓出成功。

從源碼中,我們可以學到不少編程技巧。 比如我們也可以使用  自旋+Thread.Yeild()   或 while+Thread.Yeild() 等組合。


五:總結

本章談了自旋鎖的基礎+樓主的經驗。  SpinLock類源碼這塊,只粗淺理解了下,并沒有深究。

測了下SpinLock和自己實現的自旋鎖性能對比(并行添加1000w List<int>()),SpinLock是單純的自旋鎖性能2倍以上。

還測了下lock的性能,是系統SpinLock性能的3倍以上。  可見lock內部自旋的效率更高,CLR暫沒開源,所以看不到CLR具體實現的代碼。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品激情国产视频| 国产欧美日韩中文| 国产偷国产偷亚洲清高网站| 日韩中文字幕在线观看| 欧美日韩电影在线观看| 日韩在线视频中文字幕| 日本精品在线视频| 91tv亚洲精品香蕉国产一区7ujn| 欧美日本高清视频| 成人国产精品久久久| 欧美激情免费在线| 国产精品人成电影| 日韩免费精品视频| 中文字幕国产精品| 亚洲伊人第一页| 日韩在线播放av| 国产精品久久久久9999| 国产精品色婷婷视频| 欧美国产日韩一区二区| 久久资源免费视频| 久久久久北条麻妃免费看| 欧美国产极速在线| 国产精品日日摸夜夜添夜夜av| 日韩成人中文电影| 久久久久这里只有精品| 精品高清美女精品国产区| 亚洲free性xxxx护士白浆| 国产中文日韩欧美| 日本一区二区不卡| 精品国产电影一区| 久久久99免费视频| 欧美日韩亚洲一区二区| 国产精品h片在线播放| 亚洲精品视频二区| 91精品在线影院| 国产精品91久久久| 久久视频国产精品免费视频在线| 国产精品最新在线观看| 日韩电影视频免费| 欧美美女18p| 国产大片精品免费永久看nba| 一个色综合导航| 日韩在线观看网站| 国产精品免费在线免费| 日本成人激情视频| 国产精品极品尤物在线观看| 欧美大片在线影院| 久久黄色av网站| 国产精品直播网红| 成人黄色av免费在线观看| 亚洲人成网站777色婷婷| 亚洲精品有码在线| 久久人人爽国产| 亚洲少妇中文在线| 国产精品欧美激情| 欧美激情视频一区| 久久精品在线视频| 亚洲精品白浆高清久久久久久| 日日噜噜噜夜夜爽亚洲精品| 成人欧美一区二区三区黑人孕妇| 亚洲国产精品悠悠久久琪琪| 欧美成人午夜视频| 欧美国产第二页| 欧美激情在线播放| 欧洲亚洲在线视频| 欧美性资源免费| 91精品久久久久久久久久久久久久| 国产精品久久久久999| 欧美成人h版在线观看| 国产美女搞久久| 亚洲精品免费在线视频| 亚洲在线视频福利| 久久久精品999| 深夜福利国产精品| 日韩在线视频观看| 97视频免费看| 日韩精品久久久久久福利| 97视频免费观看| 欧美日韩激情视频8区| 欧美疯狂xxxx大交乱88av| 成人午夜黄色影院| 欧美大尺度在线观看| 欧美国产极速在线| 九九久久久久久久久激情| 亚洲欧洲一区二区三区久久| 欧美在线性爱视频| 久久av红桃一区二区小说| 日韩免费电影在线观看| 国产精品久久久91| 欧美黄网免费在线观看| 亚洲字幕一区二区| 国产精品美女无圣光视频| 日韩在线视频网站| 欧洲永久精品大片ww免费漫画| 成人一区二区电影| 欧美性猛xxx| 成人在线激情视频| 日韩www在线| 91久久精品美女| 国产视频综合在线| 欲色天天网综合久久| 国产一区二区三区视频在线观看| 国产欧美一区二区三区久久人妖| 亚洲国产精品福利| www.欧美精品| 狠狠色狠狠色综合日日小说| 日韩在线观看电影| 日韩精品极品在线观看| 不卡中文字幕av| 亚洲热线99精品视频| 福利视频导航一区| 亚洲精品福利资源站| 成人av在线网址| 中文字幕亚洲欧美一区二区三区| 欧美成年人视频网站欧美| 日韩中文字幕国产| 555www成人网| 亚洲一区二区久久久久久久| 亚洲天堂av在线免费观看| 这里只有精品在线观看| 国产精品欧美激情| 91影院在线免费观看视频| 性欧美xxxx视频在线观看| 欧美一级淫片丝袜脚交| 久久成人18免费网站| 亚洲精品成a人在线观看| 亚洲网站视频福利| 国产香蕉97碰碰久久人人| 91深夜福利视频| 国产69精品久久久| 久久理论片午夜琪琪电影网| 色偷偷偷亚洲综合网另类| 久久久久久美女| 韩国视频理论视频久久| 日产日韩在线亚洲欧美| 91久久精品国产91性色| 一本色道久久88亚洲综合88| 亚洲国产福利在线| 欧美肥臀大乳一区二区免费视频| 国产视频福利一区| 国产精品久久久久免费a∨大胸| 91免费人成网站在线观看18| 欧美国产亚洲视频| 九九久久综合网站| 亚洲欧美制服综合另类| 亚洲性线免费观看视频成熟| 欧美国产视频一区二区| 久久久精品一区| 日韩大陆欧美高清视频区| 欧美另类99xxxxx| 91精品国产91久久久久久不卡| 亚洲偷欧美偷国内偷| 亚洲午夜国产成人av电影男同| 国产精品视频白浆免费视频| 精品网站999www| 亚洲男人第一av网站| 日韩在线免费视频| 成人激情综合网| 人妖精品videosex性欧美| 日韩av电影手机在线| 国产免费一区二区三区香蕉精| 亚洲第一网站男人都懂| 欧美激情a∨在线视频播放| 国产日韩欧美在线视频观看|