程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#多線程編程中的鎖體系(二)

C#多線程編程中的鎖體系(二)

編輯:C#入門知識

C#多線程編程中的鎖體系(二)。本站提示廣大學習愛好者:(C#多線程編程中的鎖體系(二))文章只能為提供參考,不一定能成為您想要的結果。以下是C#多線程編程中的鎖體系(二)正文


上章重要講排他鎖的直接應用方法。但現實傍邊全體都用鎖又太糟蹋了,或許排他鎖粒度太年夜了。 這一次我們說說進級鎖和原子操作。

目次
1:volatile
2:  Interlocked
3:ReaderWriterLockSlim
4:總結

一:volatile

簡略來講: volatile症結字是告知c#編譯器和JIT編譯器,纰謬volatile標志的字段做任何的緩存。確保字段讀寫都是原子操作,最新值。

這不就是鎖嗎?   其這貨它基本不是鎖, 它的原子操作是基於CPU自己的,非壅塞的。 由於32位CPU履行賦值指令,數據傳輸最年夜寬度4個字節。

所以只需在4個字節以下讀寫操作的,32位CPU都是原子操作。volatile 它就是應用這個特征來的。

好殘暴的現實?否則,微軟年夜法如許是為了進步JIT機能效力,對有些數據停止緩存了(多線程下)。


  //准確
       public volatile Int32 score1 = 1;
        //報錯
        public volatile Int64 score2 = 1;

看下面的例子,我們界說8個字節長度score2就不可了。  由於8個字節,32位CPU就分紅2個指令履行了。天然就沒法包管原子操作了。

這麼細節的,忘了怎樣辦,那豈不是坑人啊。  因而微軟年夜法直接一棍子打逝世,限制4個字節以下的類型字段能力用volatile,詳細甚麼、看msdn吧。

那明天我曉得了。我編譯平台改成64位上,只在64位CPU用volatile  int64,行不可?  不可,編譯器報錯。說了一棍子打逝世了。。

(^._.^)ノ  好吧,其實可以用IntPtr這個。

 volatile多半情形下很有效處的,究竟鎖的機能開支照樣很年夜的。我們可以把當做輕量級的鎖,依據詳細場景公道應用,能進步很多法式機能。

線程中的Thread.VolatileRead 和Thread.VolatileWrite 就是volatile的龐雜版。

二:Interlocked

MSDN 描寫:為多個線程同享的變量供給原子操作。重要函數以下:

Interlocked.Increment    原子操作,遞增指定變量的值並存儲成果。
Interlocked.Decrement       原子操作,遞加指定變量的值並存儲成果。
Interlocked.Add        原子操作,添加兩個整數並用二者的和調換第一個整數

Interlocked.CompareExchange(ref a, b, c);  原子操作,a參數和c參數比擬,  相等b調換a,不相等不調換。

根本用法就不多說了。直接來段CLR via C# interlock anything的例子:

public static int Maximum(ref int target, int value)
        {
            int currentVal = target, startVal, desiredVal;  //記載前後值
            do
            {
                startVal = currentVal; //記載輪回迭代的初始值。
                desiredVal = Math.Max(startVal, value); //基於startVal和value盤算希冀值desiredVal

                //高並發下,線程被搶占情形下,target值會產生轉變。

                //target startVal相等解釋沒轉變。desiredVal 直代替換。
                currentVal = Interlocked.CompareExchange(ref target, desiredVal, startVal);

            } while (startVal != currentVal); //不相等解釋,target值曾經被其他線程修改。自旋持續。
            return desiredVal;
        }

三:ReaderWriterLockSlim

假設我們有份緩存數據A,假如每次都不論任何操作lock一下,那末我的這份緩存A就永久只能單線程讀寫了, 這在Web高並發下是不克不及忍耐的。

那有無一種方法我只在寫入時進入獨有鎖呢,讀操作時不限制線程數目呢?謎底就是我們的ReaderWriterLockSlim配角,讀寫鎖。

ReaderWriterLockSlim 個中一種鎖EnterUpgradeableReadLock最症結  便可進級鎖。 

它呢許可你先輩入讀鎖,發明緩存A紛歧樣了, 再進入寫鎖,寫入撤退退卻回讀鎖形式。

ps: 這裡留意下net 3.5之前有個ReaderWriterLock 機能較差。推舉應用進級版的 ReaderWriterLockSlim 。

//實例一個讀寫鎖
 ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);

下面實例一個讀寫鎖,這裡留意的是結構函數的列舉。

LockRecursionPolicy.NoRecursion 不支撐,發明遞歸會拋異常。

LockRecursionPolicy.SupportsRecursion  即支撐遞歸形式,線程鎖中持續在應用鎖。


cacheLock.EnterReadLock();
            //do
                cacheLock.EnterReadLock();
                //do
                cacheLock.ExitReadLock();
            cacheLock.ExitReadLock();

這類形式極易輕易逝世鎖,好比讀鎖外面應用寫鎖。

cacheLock.EnterReadLock();
            //do
              cacheLock.EnterWriteLock();
              //do
              cacheLock.ExitWriteLock();
            cacheLock.ExitReadLock();

上面是直接拿msdn的緩存例子了,加了簡略正文。

public class SynchronizedCache
    {
        private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
        private Dictionary<int, string> innerCache = new Dictionary<int, string>();

        public string Read(int key)
        {
            //進入讀鎖,許可其他一切的讀線程,寫入線程被壅塞。
            cacheLock.EnterReadLock();
            try
            {
                return innerCache[key];
            }
            finally
            {
                cacheLock.ExitReadLock();
            }
        }

        public void Add(int key, string value)
        {
            //進入寫鎖,其他一切拜訪操作的線程都被壅塞。即寫獨有鎖。
            cacheLock.EnterWriteLock();
            try
            {
                innerCache.Add(key, value);
            }
            finally
            {
                cacheLock.ExitWriteLock();
            }
        }

        public bool AddWithTimeout(int key, string value, int timeout)
        {
            //超時設置,假如在超不時間內,其他寫鎖還不釋放,就廢棄操作。
            if (cacheLock.TryEnterWriteLock(timeout))
            {
                try
                {
                    innerCache.Add(key, value);
                }
                finally
                {
                    cacheLock.ExitWriteLock();
                }
                return true;
            }
            else
            {
                return false;
            }
        }

        public AddOrUpdateStatus AddOrUpdate(int key, string value)
        {
            //進入進級鎖。 同時只能有一個可進級鎖線程。寫鎖,進級鎖都被壅塞,但許可其他讀取數據的線程。
            cacheLock.EnterUpgradeableReadLock();
            try
            {
                string result = null;
                if (innerCache.TryGetValue(key, out result))
                {
                    if (result == value)
                    {
                        return AddOrUpdateStatus.Unchanged;
                    }
                    else
                    {
                        //進級成寫鎖,其他一切線程都被壅塞。
                        cacheLock.EnterWriteLock();
                        try
                        {
                            innerCache[key] = value;
                        }
                        finally
                        {
                            //加入寫鎖,許可其他讀線程。
                            cacheLock.ExitWriteLock();
                        }
                        return AddOrUpdateStatus.Updated;
                    }
                }
                else
                {
                    cacheLock.EnterWriteLock();
                    try
                    {
                        innerCache.Add(key, value);
                    }
                    finally
                    {
                        cacheLock.ExitWriteLock();
                    }
                    return AddOrUpdateStatus.Added;
                }
            }
            finally
            {
                //加入進級鎖。
                cacheLock.ExitUpgradeableReadLock();
            }
        }

        public enum AddOrUpdateStatus
        {
            Added,
            Updated,
            Unchanged
        };
    }

四:總結

多線程現實開辟傍邊,常常測試沒成績,一到臨盆情況,並發高了就輕易出成績, 必定留意。

本文參考CLR via C#。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved