久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

C# 溫故而知新:線(xiàn)程篇(三)

 賈朋亮博客 2014-09-17

C# 溫故而知新: 線(xiàn)程篇(三)

線(xiàn)程同步篇 (上)

1.線(xiàn)程同步中的一些重要概念

1.1臨界區(qū)(共享區(qū))的概念

在多線(xiàn)程的環(huán)境中,,可能需要共同使用一些公共資源,這些資源可能是變量,,方法邏輯段等等,,這些被多個(gè)線(xiàn)程

共用的區(qū)域統(tǒng)稱(chēng)為臨界區(qū)(共享區(qū)),聰明的你肯定會(huì)想到,,臨界區(qū)的資源不是很安全,,因?yàn)榫€(xiàn)程的狀態(tài)是不定的,所以

可能帶來(lái)的結(jié)果是臨界區(qū)的資源遭到其他線(xiàn)程的破壞,,我們必須采取策略或者措施讓共享區(qū)數(shù)據(jù)在多線(xiàn)程的環(huán)境下保持

完成性不讓其受到多線(xiàn)程訪(fǎng)問(wèn)的破壞

1.2基元用戶(hù)模式

可能大家覺(jué)得這個(gè)很難理解,,的確如果光看概念解釋的話(huà),,會(huì)讓人抓狂的,,因?yàn)檫@個(gè)模式牽涉到了深?yuàn)W的底層cup

內(nèi)核和windows的一些底層機(jī)制,所以我用最簡(jiǎn)單的理解相信大家一定能理解,,因?yàn)檫@對(duì)于理解同步也很重要

回到正題,,基元用戶(hù)模式是指使用cpu的特殊指令來(lái)調(diào)度線(xiàn)程,所以這種協(xié)調(diào)調(diào)度線(xiàn)程是在硬件中進(jìn)行的所以得出

了它第一些優(yōu)點(diǎn):

速度特別快

線(xiàn)程阻塞時(shí)間特別短

但是由于該模式中的線(xiàn)程可能被系統(tǒng)搶占,,導(dǎo)致該模式中的線(xiàn)程為了獲取某個(gè)資源,,而浪費(fèi)許多cpu時(shí)間,同時(shí)如果一直處

于等待的話(huà)會(huì)導(dǎo)致”活鎖”,,也就是既浪費(fèi)了內(nèi)存,,又浪費(fèi)了cpu時(shí)間,這比下文中的死鎖更可怕,,那么如何利用強(qiáng)大的

cpu時(shí)間做更多的事呢,?那就引出了下面的一個(gè)模式

1.3基元內(nèi)核模式

該模式和用戶(hù)模式不同,它是windows系統(tǒng)自身提供的,,使用了操作系統(tǒng)中內(nèi)核函數(shù),,所以它能夠阻塞線(xiàn)程提高了cpu的利

用率,,同時(shí)也帶來(lái)了一個(gè)很可怕的bug,死鎖,可能線(xiàn)程會(huì)一直阻塞導(dǎo)致程序的奔潰,常用的內(nèi)核模式的技術(shù)例如Monitor,Mutex,

等等會(huì)在下一章節(jié)介紹,。本章將詳細(xì)討論鎖的概念,,使用方法和注意事項(xiàng)

*1.4原子性操作

如果一個(gè)語(yǔ)句執(zhí)行一個(gè)單獨(dú)不可分割的指令,那么它是原子的,。嚴(yán)格的原子操作排除了任何搶占的可能性(這也是實(shí)現(xiàn)同步的一

個(gè)重要條件,,也就是說(shuō)沒(méi)有一個(gè)線(xiàn)程可以把這個(gè)美女占為己有,更方便的理解是這個(gè)值永遠(yuǎn)是最新的),,在c#中原子操作如下圖所示:

其實(shí)要符合原子操作必須滿(mǎn)足以下條件

  1. c#中如果是32位cpu的話(huà),,為一個(gè)少于等于32位字段賦值是原子操作其他(自增,,讀,,寫(xiě)操作)的則不是
  2. 對(duì)于64位cpu而言,操作32或64位的字段賦值都屬于原子操作
  3. 其他讀寫(xiě)操作都不能屬于原子操作

相信大家能夠理解原子的特點(diǎn),,下文中的Volatil和interlocked會(huì)詳細(xì)模擬原子操作來(lái)實(shí)現(xiàn)線(xiàn)程同步,,所以在使用原子操

作時(shí)也需要注意當(dāng)前操作系統(tǒng)是32位或是64位cpu或者兩者皆要考慮

1.5非阻止同步

非阻止同步說(shuō)到底,就是利用原子性操作實(shí)現(xiàn)線(xiàn)程間的同步,,不刻意阻塞線(xiàn)程,,減少相應(yīng)線(xiàn)程的開(kāi)銷(xiāo),下文中的VolatileRead,V

olatileWrite,Volatile關(guān)鍵字,,interlocked類(lèi)便是c#中非阻止同步的理念所產(chǎn)生的線(xiàn)程同步技術(shù)

1.6阻止同步

阻止同步正好相反,,其實(shí)阻止同步也是基元內(nèi)核模式的特點(diǎn)之一,例如c# 中的鎖機(jī)制,,及其下幾章介紹的mutex,monitor等都屬

于阻止同步,,他們的根本目的是,以互斥的效果讓同一時(shí)間只有一個(gè)線(xiàn)程能夠訪(fǎng)問(wèn)共享區(qū),,其他線(xiàn)程必須阻止等待,,直到該線(xiàn)程離開(kāi)共享

區(qū)后,在讓其他一個(gè)線(xiàn)程訪(fǎng)問(wèn)共享區(qū),,阻止同步缺點(diǎn)也是容易產(chǎn)生死鎖,,但是阻止同步提高了cpu時(shí)間的利用率

2.詳解Thread類(lèi)中的VolatileRead和VolatileWrite方法和Volatile關(guān)鍵字

前文中,我們已經(jīng)對(duì)原子操作和非阻止同步的概念已經(jīng)有了大概的認(rèn)識(shí),,接著讓我們從新回到Thread類(lèi)來(lái)看下其中比較經(jīng)典的VolatileRead

和VolatileWrite方法

VolatileWrite

該方法作用是,,當(dāng)線(xiàn)程在共享區(qū)(臨界區(qū))傳遞信息時(shí),通過(guò)此方法來(lái)原子性的寫(xiě)入最后一個(gè)值

VolatileRead:

該方法作用是,,當(dāng)線(xiàn)程在共享區(qū)(臨界區(qū))傳遞信息時(shí),,通過(guò)此方法來(lái)原子性的讀取第一個(gè)值

可能這樣的解釋會(huì)讓大家困惑,老規(guī)矩,,直接上例子讓大家能夠理解:

復(fù)制代碼
   /// <summary>
    /// 本例利用VolatileWrite和VolatileRead來(lái)實(shí)現(xiàn)同步,,來(lái)實(shí)現(xiàn)一個(gè)計(jì)算
    /// 的例子,每個(gè)線(xiàn)程負(fù)責(zé)運(yùn)算1000萬(wàn)個(gè)數(shù)據(jù),,共開(kāi)啟10個(gè)線(xiàn)程計(jì)算至1億,,
    /// 而且每個(gè)線(xiàn)程都無(wú)法干擾其他線(xiàn)程工作
    /// </summary>
    class Program
    {
        static Int32 count;//計(jì)數(shù)值,用于線(xiàn)程同步 (注意原子性,,所以本例中使用int32)
        static Int32 value;//實(shí)際運(yùn)算值,,用于顯示計(jì)算結(jié)果
        static void Main(string[] args)
        {
            //開(kāi)辟一個(gè)線(xiàn)程專(zhuān)門(mén)負(fù)責(zé)讀value的值,這樣就能看見(jiàn)一個(gè)計(jì)算的過(guò)程
            Thread thread2 = new Thread(new ThreadStart(Read));
            thread2.Start();
            //開(kāi)辟10個(gè)線(xiàn)程來(lái)負(fù)責(zé)計(jì)算,,每個(gè)線(xiàn)程負(fù)責(zé)1000萬(wàn)條數(shù)據(jù)
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(20);
                Thread thread = new Thread(new ThreadStart(Write));
                thread.Start();
            }
            Console.ReadKey();
        }

        /// <summary>
        /// 實(shí)際運(yùn)算寫(xiě)操作
        /// </summary>
        private static void Write()
        {
            Int32 temp = 0;
            for (int i = 0; i < 10000000; i++)
            {
                temp += 1;
            }
            value += temp;
            //注意VolatileWrite 在每個(gè)線(xiàn)程計(jì)算完畢時(shí)會(huì)寫(xiě)入同步計(jì)數(shù)值為1,,告訴程序該線(xiàn)程已經(jīng)執(zhí)行完畢
            //所以VolatileWrite方法類(lèi)似與一個(gè)按鈴,往往在原子性的最后寫(xiě)入告訴程序我完成了
            Thread.VolatileWrite(ref count, 1);
        }

        /// <summary>
        ///  顯示計(jì)算后的數(shù)據(jù),,使用該方法的線(xiàn)程會(huì)死循環(huán)等待寫(xiě)
        ///  操作的線(xiàn)程發(fā)出完畢信號(hào)后顯示當(dāng)前計(jì)算結(jié)果
        /// </summary>
        private static void Read()
        {
            while (true)
            {
                //一旦監(jiān)聽(tīng)到一個(gè)寫(xiě)操作線(xiàn)執(zhí)行完畢后立刻顯示操作結(jié)果
                //和VolatileWrite相反,,VolatileRead類(lèi)似一個(gè)門(mén)禁,只有原子性的最先讀取他,,才能達(dá)到同步效果
                //同時(shí)count值保持最新
                if (Thread.VolatileRead(ref count) > 0)
                {
                    Console.WriteLine("累計(jì)計(jì)數(shù):{1}", Thread.CurrentThread.ManagedThreadId, value);
                    //將count設(shè)置成0,,等待另一個(gè)線(xiàn)程執(zhí)行完畢
                    count = 0;
                }
            }
        }


    }
復(fù)制代碼

顯示結(jié)果:

例子中我們可以看出當(dāng)個(gè)線(xiàn)程調(diào)用Read方法時(shí),代碼會(huì)先判斷Thread. VolatileRead先讀取計(jì)數(shù)值是否返回正確的計(jì)數(shù)值,,如果正確則顯示

結(jié)果,,不正確的話(huà)繼續(xù)循環(huán)等待,而這個(gè)返回值是通過(guò)其他線(xiàn)程操作Write方法時(shí)最后寫(xiě)入的,,也就是說(shuō)對(duì)于Thread. VolatileWrite

方法的作用便一目了然了,,在實(shí)現(xiàn)Thread. VolatileWrite寫(xiě)入其他的數(shù)據(jù)或進(jìn)行相應(yīng)的邏輯處理,在我們示例代碼中我們會(huì)先去加運(yùn)算到

10000000時(shí),,通過(guò)thread. VolatileWrite原子性的操作寫(xiě)入計(jì)數(shù)值告訴那個(gè)操作Read方法的線(xiàn)程有一個(gè)計(jì)算任務(wù)已經(jīng)完成,,于是死循環(huán)中

的Thread. VolatileRead方法接受到了信號(hào),你可以顯示計(jì)算結(jié)果了,,于是結(jié)果便會(huì)被顯示,,同時(shí)計(jì)數(shù)值歸零,,這樣便起到了一個(gè)非阻塞功能

的同步效果,,同樣對(duì)于臨界區(qū)(此例中的Write方法體和Read方法體)起到了保護(hù)的作用。當(dāng)然由于使用上述兩個(gè)方法在復(fù)雜的項(xiàng)目中很容易

出錯(cuò),,往往這種錯(cuò)誤是很難被發(fā)現(xiàn),,所以微軟為了讓我們更好使用,便開(kāi)發(fā)出了一個(gè)新的關(guān)鍵字Volatile

Volatile關(guān)鍵字的作用

Volatile關(guān)鍵字的本質(zhì)含義是告訴編譯器,,聲明為Volatile關(guān)鍵字的變量或字段都是提供給多個(gè)線(xiàn)程使用的,,當(dāng)然不是每個(gè)類(lèi)型都

可以聲明為Volatile類(lèi)型字段,msdn中詳細(xì)說(shuō)明了那些類(lèi)型可以聲明為Volatile 所以不再陳述,但是有一點(diǎn)必須注意,,Volatile

無(wú)法聲明為局部變量,。作為原子性的操作Volatile關(guān)鍵字具有原子特性,,所以線(xiàn)程間無(wú)法對(duì)其占有,,它的值永遠(yuǎn)是最新的。那我

們就對(duì)上文的那個(gè)例子簡(jiǎn)化如下:

View Code
    /// <summary>
    /// 本例利用volatile關(guān)鍵字來(lái)實(shí)現(xiàn)同步,,來(lái)實(shí)現(xiàn)一個(gè)計(jì)算
    /// 的例子,,每個(gè)線(xiàn)程負(fù)責(zé)運(yùn)算1000萬(wàn)個(gè)數(shù)據(jù),共開(kāi)啟10個(gè)線(xiàn)程計(jì)算至1億,,
    /// 而且每個(gè)線(xiàn)程都無(wú)法干擾其他線(xiàn)程工作
    /// </summary>
    class Program
    {
        static volatile Int32 count;//計(jì)數(shù)值,,用于線(xiàn)程同步 (注意原子性,所以本例中使用int32)
        static Int32 value;//實(shí)際運(yùn)算值,,用于顯示計(jì)算結(jié)果
        static void Main(string[] args)
        {
            //開(kāi)辟一個(gè)線(xiàn)程專(zhuān)門(mén)負(fù)責(zé)讀value的值,,這樣就能看見(jiàn)一個(gè)計(jì)算的過(guò)程
            Thread thread2 = new Thread(new ThreadStart(Read));
            thread2.Start();
            //開(kāi)辟10個(gè)線(xiàn)程來(lái)負(fù)責(zé)計(jì)算,每個(gè)線(xiàn)程負(fù)責(zé)1000萬(wàn)條數(shù)據(jù)
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(20);
                Thread thread = new Thread(new ThreadStart(Write));
                thread.Start();
            }
            Console.ReadKey();
        }

        /// <summary>
        /// 實(shí)際運(yùn)算寫(xiě)操作
        /// </summary>
        private static void Write()
        {
            Int32 temp = 0;
            for (int i = 0; i < 10000000; i++)
            {
                temp += 1;
            }
            value += temp;
            //注意VolatileWrite 在每個(gè)線(xiàn)程計(jì)算完畢時(shí)會(huì)寫(xiě)入同步計(jì)數(shù)值為1,,告訴程序該線(xiàn)程已經(jīng)執(zhí)行完畢
            //將count值設(shè)置成1,,效果等同于Thread.VolatileWrite
            count = 1;
        }

        /// <summary>
        ///  顯示計(jì)算后的數(shù)據(jù),使用該方法的線(xiàn)程會(huì)死循環(huán)等待寫(xiě)
        ///  操作的線(xiàn)程發(fā)出完畢信號(hào)后顯示當(dāng)前計(jì)算結(jié)果
        /// </summary>
        private static void Read()
        {
            while (true)
            {
                //一旦監(jiān)聽(tīng)到一個(gè)寫(xiě)操作線(xiàn)執(zhí)行完畢后立刻顯示操作結(jié)果,效果等同于Thread.VolatileRead
                if (count==1)
                {
                    Console.WriteLine("累計(jì)計(jì)數(shù):{1}", Thread.CurrentThread.ManagedThreadId, value);
                    //將count設(shè)置成0,,等待另一個(gè)線(xiàn)程執(zhí)行完畢
                    count = 0;
                }
            }
        }


    }

從例子中大家可以看出Volatile關(guān)鍵字的出現(xiàn)替代了原先VolatileRead 和VolatileWrite方法的繁瑣,,同時(shí)原子性的操作更加直觀(guān)透明

3.詳解Interlocked

相信大家理解了Volatile后對(duì)于非阻止同步和原子操作有了更深的認(rèn)識(shí),接下來(lái)的Interlocked雖然也屬于非阻止同步但是而后Volatile相比也

有著很大的不同,,interlocked 利用了一個(gè)計(jì)數(shù)值的概念來(lái)實(shí)現(xiàn)同步,,當(dāng)然這個(gè)計(jì)數(shù)值也是屬于原子性的操作每個(gè)線(xiàn)程都有機(jī)會(huì)通過(guò)Interlocked

去遞增或遞減這個(gè)計(jì)數(shù)值來(lái)達(dá)到同步的效果,同時(shí)Interlocked比Volatile更加適應(yīng)復(fù)雜的邏輯和并發(fā)的情況

首先讓我們了解下Interlocked類(lèi)的一些重要方法

static long Read()

以原子操作形式讀取計(jì)數(shù)值,,該方法能夠讀取當(dāng)前計(jì)數(shù)值,,但是如果是64位cpu的可以不需要使用該方法讀取.

*但是如果是32位的cpu則必須使用interlocked類(lèi)的方法對(duì)64位的變量進(jìn)行操作來(lái)保持原子操作,否則就不是原子操作

static int or long Increment(Int32 Or Int64)

該方法已原子操作的形式遞增指定變量的值并存儲(chǔ)結(jié)果,,也可以理解成以原子的操作對(duì)計(jì)數(shù)器加1

Increment有2個(gè)返回類(lèi)型的版本,,分別是int 和 long

static int or long Decrement(Int32 Or Int64)

和Increment方法相反,該方法已原子操作的形式遞減指定變量的值并存儲(chǔ)結(jié)果,,也可以理解成以原子的操作對(duì)計(jì)數(shù)器減1

同樣,,Decrement也有2個(gè)返回類(lèi)型的版本,分別是int 和 long

static int Add(ref int location1,int value)

該方法是將Value的值和loation1中的值相加替換location1中原有值并且存儲(chǔ)在locaion1中,,注意,,該方法不會(huì)拋出溢出異常

如果location中的值和Value之和大于int32.Max則,,location1中的值會(huì)變成int32.Min和Value之和

Exchange(double location1,double value)

Exchange方法有多個(gè)重載,,但是使用方法是一致的,,以原子操作的形式將Value的值賦值給location1

看完了概念性的介紹后,讓我們馬上進(jìn)入很簡(jiǎn)單的一個(gè)示例,,來(lái)深刻理解下Interlocked的使用方法

復(fù)制代碼
  /// <summary>
    /// 本示例通過(guò)Interlocked實(shí)現(xiàn)同步示例,,通過(guò)Interlocked.Increment和
    /// Interlocked.Decrement來(lái)實(shí)現(xiàn)同步,此例有2個(gè)共享區(qū),,一個(gè)必須滿(mǎn)足計(jì)數(shù)值為0,,另
    /// 一個(gè)滿(mǎn)足計(jì)數(shù)值為1時(shí)才能進(jìn)入
    /// </summary>
    class Program
    {
        //聲明計(jì)數(shù)變量
        //(注意這里用的是long是64位的,所以在32位機(jī)子上一定要通過(guò)Interlocked來(lái)實(shí)現(xiàn)原子操作)
        static long _count = 0;

        static void Main(string[] args)
        {
            //開(kāi)啟6個(gè)線(xiàn)程,,3個(gè)執(zhí)行Excution1,,三個(gè)執(zhí)行Excution2
            for (int i = 0; i < 3; i++)
            {
                Thread thread = new Thread(new ThreadStart(Excution1));
                Thread thread2 = new Thread(new ThreadStart(Excution2));
                thread.Start();
                Thread.Sleep(10);
                thread2.Start();
                Thread.Sleep(10);
            }
            //這里和同步無(wú)關(guān),只是簡(jiǎn)單的對(duì)Interlocked方法進(jìn)行示例
            Interlocked.Add(ref _count, 2);
            Console.WriteLine("為當(dāng)前計(jì)數(shù)值加上一個(gè)數(shù)量級(jí):{0}后,,當(dāng)前計(jì)數(shù)值為:{1}", 2, _count);
            Interlocked.Exchange(ref _count, 1);
            Console.WriteLine("將當(dāng)前計(jì)數(shù)值改變后,,當(dāng)前計(jì)數(shù)值為:{0}", _count);
            Console.Read();
        }

        static void Excution1()
        {
            //進(jìn)入共享區(qū)1的條件
            if (Interlocked.Read(ref _count) == 0)
            {
                Console.WriteLine("Thread ID:{0} 進(jìn)入了共享區(qū)1", Thread.CurrentThread.ManagedThreadId);
                //原子性增加計(jì)數(shù)值,讓其他線(xiàn)程進(jìn)入共享區(qū)2
                Interlocked.Increment(ref _count);
                Console.WriteLine("此時(shí)計(jì)數(shù)值Count為:{0}", Interlocked.Read(ref _count));
            }
        }

        static void Excution2()
        {
            //進(jìn)入共享區(qū)2的條件
            if (Interlocked.Read(ref _count) == 1)
            {
                Console.WriteLine("Thread ID:{0} 進(jìn)入了共享區(qū)2", Thread.CurrentThread.ManagedThreadId);
                //原子性減少計(jì)數(shù)值,,讓其他線(xiàn)程進(jìn)入共享區(qū)1
                Interlocked.Decrement(ref _count);
                Console.WriteLine("此時(shí)計(jì)數(shù)值Count為:{0}", Interlocked.Read(ref _count));
            }
        }
    }
復(fù)制代碼

在本例中,,我們使用和上文一樣的思路,通過(guò)不同線(xiàn)程來(lái)原子性的操作計(jì)數(shù)值來(lái)達(dá)到同步效果,,大家可以仔細(xì)觀(guān)察到,,通過(guò)

Interlocked對(duì)計(jì)數(shù)值進(jìn)行操作就能夠讓我們非常方便的使用非阻止的同步效果了,但是在復(fù)雜的項(xiàng)目或邏輯中,,可能也會(huì)出

錯(cuò)導(dǎo)致活鎖的可能,,大家務(wù)必當(dāng)心

4.介紹下Lock關(guān)鍵字

Lock關(guān)鍵字是用來(lái)對(duì)于多線(xiàn)程中的共享區(qū)進(jìn)行阻止同步的一種方案當(dāng)某一個(gè)線(xiàn)程進(jìn)入臨界區(qū)時(shí),,lock關(guān)鍵字會(huì)鎖住共享區(qū),

同樣可以理解為互斥段,,互斥段在某一時(shí)刻內(nèi)只允許一個(gè)線(xiàn)程進(jìn)入,同時(shí)編譯器會(huì)把這個(gè)關(guān)鍵字編譯成Monitor.Entery和

Monitor.Exit 方法,,關(guān)于Monitor類(lèi)會(huì)在下章詳細(xì)闡述,。既然有Lock關(guān)鍵字,那么它是如何工作的,?到底鎖住了什么,,怎么

高效和正確的使用lock關(guān)鍵字呢?

其實(shí)鎖的概念還是來(lái)自于現(xiàn)實(shí)生活,,共享區(qū)就是多個(gè)人能夠共同擁有房間,,當(dāng)其中一個(gè)人進(jìn)入房間后,他把鎖反鎖,,直到他解鎖

出門(mén)后將鑰匙交給下個(gè)人,,可能房間的門(mén)可能有問(wèn)題,或者進(jìn)入房間的人因?yàn)槟撤N原因出不來(lái)了,,導(dǎo)致全部的人都無(wú)法進(jìn)去,,這些

問(wèn)題也是我們應(yīng)該考慮到的,好,,首先讓我們討論下我們應(yīng)該Lock住什么,,什么材料適合當(dāng)鎖呢?

雖然說(shuō)lock關(guān)鍵字可以鎖住任何object類(lèi)型及其派生類(lèi),,但是盡量不要用public 類(lèi)型的,,因?yàn)閜ublic類(lèi)型難以控制

有可能大伙對(duì)上面的有點(diǎn)疑問(wèn),為什么不能用public類(lèi)型的呢,,為什么會(huì)難以控制呢,?

好,以下3個(gè)例子是比較經(jīng)典的例證

1.Lock(this):大伙肯定會(huì)知道this指的是當(dāng)前類(lèi)對(duì)象,Lock(this) ,、意味著將當(dāng)前類(lèi)對(duì)象給鎖住了,,

假設(shè)我需要同時(shí)使用這個(gè)類(lèi)的別的方法,那么某一線(xiàn)程一旦進(jìn)入臨界區(qū)后,,那完蛋了,,該類(lèi)所有的

成員(方法)都無(wú)法訪(fǎng)問(wèn),這可能在某些時(shí)刻是致命的錯(cuò)誤

2.同理Lock(typeof(XXX)) 更厲害,,一方面對(duì)鎖的性能有很大影響,,因?yàn)橐粋€(gè)類(lèi)型太大了,其次,,

當(dāng)某一線(xiàn)程進(jìn)入臨界區(qū)后,,包括所有該類(lèi)型的type都可能會(huì)被鎖住而產(chǎn)生死鎖

3.最嚴(yán)重的某過(guò)于鎖住字符串對(duì)象,lock(“Test”),c#中的字符串對(duì)象很特殊,,string test=”Test”

和 string test2=”Test” 其實(shí)是一個(gè)對(duì)象,,假如你使用了lock(“Test”)那么,所有字符串值為"Test"的

字符串都有可能被鎖住,,甚至造成死鎖,,所以有些奇怪的bug都是因?yàn)橐恍┖?jiǎn)單的細(xì)節(jié)導(dǎo)致

接著這個(gè)例子便是lock(this)的一個(gè)示例,既能讓大伙了解如何使用Lock關(guān)鍵字,,更是讓大伙了解,,lock(this)的危害性

復(fù)制代碼
    /// <summary>
    /// 本例展示下如何使用lock關(guān)鍵字和lock(this)時(shí)產(chǎn)生死鎖的情況
    /// </summary>
    class Program
    {

        static void Main(string[] args)
        {
            //創(chuàng)建b對(duì)象,演示lock
            B b = new B();
            Console.ReadKey();
        }
    }

    /// <summary>
    /// A類(lèi)構(gòu)造中初始化一個(gè)線(xiàn)程并且啟動(dòng),,
    /// 線(xiàn)程調(diào)用的方法內(nèi)放入死循環(huán),,并且在死循環(huán)中放入lock(this),
    /// </summary>
    public class A
    {
        public A()
        {
            Thread th = new Thread(new ThreadStart
                (
                   () =>
                   {
                       while (true)
                       {
                           lock (this)
                           {
                               Console.WriteLine("進(jìn)入a類(lèi)共享區(qū)");
                               Thread.Sleep(3000);
                           }
                       }
                   }
                ));
            th.Start();
        }
    }

    /// <summary>
    ///  B類(lèi)在構(gòu)造中創(chuàng)建A的對(duì)象,,并且還是鎖住a對(duì)象,,這樣就創(chuàng)建的死鎖的條件
    ///  因?yàn)槌跏蓟疉類(lèi)對(duì)象時(shí),A類(lèi)的構(gòu)造函數(shù)會(huì)鎖住自身對(duì)象,,這樣在A類(lèi)死循環(huán)間隔期,,一旦出了 A類(lèi)中的鎖時(shí)
    ///  進(jìn)入B的鎖住的區(qū)域內(nèi),,A 對(duì)象永遠(yuǎn)無(wú)法進(jìn)入a類(lèi)共享區(qū),從而產(chǎn)生了死鎖
    /// </summary>
    public class B
    {
        public B()
        {
            A a = new A();
            lock (a)
            {
                Console.WriteLine(@"將a類(lèi)對(duì)象鎖住的話(huà),,a類(lèi)中的lock將進(jìn)入死鎖,,
                直到3秒后B類(lèi)中的將a類(lèi)對(duì)象釋放鎖,如果我不釋放,那么 a類(lèi)中將永遠(yuǎn)無(wú)法進(jìn)入a類(lèi)共享區(qū)");
                //計(jì)時(shí)器
                Timer timer = new Timer(new TimerCallback
                    (
                      (obj) => { Console.WriteLine(DateTime.Now); }
                    ), this, 0, 1000);
                //如果這里運(yùn)行很長(zhǎng)時(shí)間,, a類(lèi)中將永遠(yuǎn)無(wú)法進(jìn)入a類(lèi)共享區(qū)
                Thread.Sleep(3000000);
            }
        }
    }
復(fù)制代碼

結(jié)果計(jì)時(shí)器會(huì)一直滾動(dòng),,因?yàn)閍對(duì)象被鎖住,除非完成Thread.Sleep(3000000)后才能進(jìn)入到a共享區(qū)

由于以上的問(wèn)題,,微軟還是建議我們使用一個(gè)私有的變量來(lái)鎖定,,由于私有變量外界無(wú)法訪(fǎng)問(wèn),所以鎖住話(huà)死鎖的可能性大大下降了,。

這樣我們就能選擇正確的“門(mén)”來(lái)進(jìn)行鎖住,,但是可能還有一種可能也會(huì)造成死鎖,就是在lock內(nèi)部出現(xiàn)了問(wèn)題,,由于死鎖非常復(fù)雜,,我將在

今后的文章中專(zhuān)門(mén)寫(xiě)一篇關(guān)于死鎖的文章來(lái)深入解釋下死鎖,所以這里就對(duì)死鎖不深究了,,這里大伙了解下lock的使用方法和注意事項(xiàng)就行了,。

5.ReaderWriterLock

由于lock關(guān)鍵字對(duì)臨界區(qū)(共享區(qū))保護(hù)的非常周密,導(dǎo)致了一些功能可能會(huì)無(wú)法實(shí)現(xiàn),,假設(shè)我將某個(gè)查詢(xún)功能放置在臨界區(qū)中時(shí),,可能

當(dāng)別的線(xiàn)程在查詢(xún)臨界區(qū)中的數(shù)據(jù)時(shí),可能我的那個(gè)線(xiàn)程被阻塞了,,所以我們期望鎖能夠達(dá)到以下功能

1 首先鎖能細(xì)分為讀鎖和寫(xiě)鎖

2 能夠保證同時(shí)可以讓多個(gè)線(xiàn)程讀取數(shù)據(jù)

3 能保證同一時(shí)刻只有一個(gè)線(xiàn)程能進(jìn)行寫(xiě)操作,,也就是說(shuō),對(duì)于寫(xiě)操作,,它必須擁有獨(dú)占鎖

4 能保證一個(gè)線(xiàn)程同一時(shí)刻只能擁有寫(xiě)鎖或讀鎖中的一個(gè)

顯然lock關(guān)鍵字無(wú)法滿(mǎn)足我們的需求,,還好微軟想到了這點(diǎn),ReaderWriterLock便隆重登場(chǎng)了ReaderWriterLock能夠達(dá)到的效果是

1. 同一時(shí)刻,,它允許多個(gè)讀線(xiàn)程同時(shí)訪(fǎng)問(wèn)臨界區(qū),,或者允許單個(gè)線(xiàn)程進(jìn)行寫(xiě)訪(fǎng)問(wèn)

2. 在讀訪(fǎng)問(wèn)率很高,而且寫(xiě)訪(fǎng)問(wèn)率很低的情況下,,效率最高,,

3.它也滿(mǎn)足了同一時(shí)刻只能獲取寫(xiě)鎖或讀鎖的要求。

4. 最為關(guān)鍵的是,,ReaderWriterLock能夠保證讀線(xiàn)程鎖和寫(xiě)線(xiàn)程鎖在各自的讀寫(xiě)隊(duì)列中,,當(dāng)某個(gè)線(xiàn)程釋放了寫(xiě)鎖了,同時(shí)讀線(xiàn)程隊(duì)列中

的所有線(xiàn)程將被授予讀鎖,,同樣,,當(dāng)所有的讀鎖被釋放時(shí),,寫(xiě)線(xiàn)程隊(duì)列中的排隊(duì)的下一個(gè)線(xiàn)程將被授予寫(xiě)鎖,更直觀(guān)的說(shuō),ReaderWriterLock

就是在這幾種狀態(tài)間來(lái)回切換

5 使用時(shí)注意每當(dāng)你使用AcquireXXX方法獲取鎖時(shí),,必須使用ReleaseXXX方法來(lái)釋放鎖

6 ReaderWriterLock 支持遞歸鎖,,關(guān)于遞歸鎖會(huì)在今后的章節(jié)詳細(xì)闡述

7 在性能方面ReaderWriterLock做的不夠理想,,和lock比較差距明顯,,而且該類(lèi)庫(kù)中還隱藏些bug,有于這些原因,微軟又專(zhuān)門(mén)重新寫(xiě)了個(gè)新

類(lèi)ReaderWriterLockSilm來(lái)彌補(bǔ)這些缺陷,。

8 處理死鎖方面ReaderWriterLock為我們提供了超時(shí)的參數(shù)這樣我們便可以有效的防止死鎖

9 對(duì)于一個(gè)個(gè)獲取了讀鎖的線(xiàn)程來(lái)說(shuō),,在寫(xiě)鎖空閑的情況下可以升級(jí)為寫(xiě)鎖

接著讓我們了解下ReaderWriterLock的重要成員

上述4個(gè)方法分別是讓線(xiàn)程獲取寫(xiě)鎖和讀鎖的方法,它利用的計(jì)數(shù)的概念,,當(dāng)一個(gè)線(xiàn)程中調(diào)用此方法后,,該類(lèi)會(huì)給該線(xiàn)程擁有的鎖計(jì)數(shù)加1

(每次加1,但是一個(gè)線(xiàn)程可以擁有多個(gè)讀鎖,,所以計(jì)數(shù)值可能更多,,但是對(duì)于寫(xiě)鎖來(lái)說(shuō)同時(shí)一個(gè)一個(gè)線(xiàn)程可以擁有)。后面的參數(shù)是超時(shí)

時(shí)間,,我們可以自己設(shè)置來(lái)避免死鎖,。同樣調(diào)用上述方法后我們必須使用ReleaseXXX 方法來(lái)讓計(jì)數(shù)值減1,直到該線(xiàn)程擁有鎖的計(jì)數(shù)為0,,

釋放了鎖為止,。

最后我們用一個(gè)簡(jiǎn)單的例子來(lái)溫故下上述的知識(shí)點(diǎn)(請(qǐng)注意看注釋)

復(fù)制代碼
    /// <summary>
    /// 該示例通過(guò)ReaderWriterLock同步來(lái)實(shí)現(xiàn)Student集合多線(xiàn)程下
    /// 的寫(xiě)操作和讀操作
    /// </summary>
    class Program
    {
        static ReaderWriterLock _readAndWriteLock = new ReaderWriterLock();
        static List<Student> demoList = new List<Student>();

        static void Main(string[] args)
        {
            InitialStudentList();
            Thread thread=null;
            for (int i = 0; i <5; i++)
            {
              
                //讓第前2個(gè)個(gè)線(xiàn)程試圖掌控寫(xiě)鎖,
                if (i < 2)
                {
                    thread = new Thread(new ParameterizedThreadStart(AddStudent));
                    Console.WriteLine("線(xiàn)程ID:{0}, 嘗試獲取寫(xiě)鎖        ", thread.ManagedThreadId);
                    thread.Start(new Student { Name = "Zhang" + i });
                }
                else 
                {
                    //讓每個(gè)線(xiàn)程都能訪(fǎng)問(wèn)DisplayStudent 方法去獲取讀鎖
                    thread = new Thread(new ThreadStart(DisplayStudent));
                    thread.Start();
                }
                Thread.Sleep(20);
            }
            Console.ReadKey();
        }

        static void InitialStudentList() 
        {
            demoList = new List<Student> { new Student{ Name="Sun"}, new Student{Name="Zheng"} };
        }

        /// <summary>
        /// 當(dāng)多個(gè)線(xiàn)程試圖使用該方法時(shí),只有一個(gè)線(xiàn)程能夠透過(guò)AcquireSWriterLock
        /// 獲取寫(xiě)鎖,,同時(shí)其他線(xiàn)程進(jìn)入隊(duì)列中等待,,直到該線(xiàn)程使用ReleaseWriterLock后
        /// 下個(gè)線(xiàn)程才能進(jìn)入擁有寫(xiě)鎖
        /// </summary>
        /// <param name="student"></param>
        static void AddStudent(object student)
        {
            if (student == null|| !(student is Student)) return;
            if (demoList.Contains(student)) return;
            try
            {
                //獲取寫(xiě)鎖
                _readAndWriteLock.AcquireWriterLock(Timeout.Infinite);
                demoList.Add(student as Student);
                Console.WriteLine("當(dāng)前寫(xiě)操作線(xiàn)程為{0},            寫(xiě)入的學(xué)生是:{1}", Thread.CurrentThread.ManagedThreadId,(student as Student).Name);
            }
            catch (Exception)
            {

            }
            finally
            {
                _readAndWriteLock.ReleaseWriterLock();
            }

        }

        /// <summary>
        /// 對(duì)于讀鎖來(lái)所,允許多個(gè)線(xiàn)程共同擁有,,所以這里同時(shí)
        /// 可能會(huì)有多個(gè)線(xiàn)程訪(fǎng)問(wèn)Student集合,,使用try catch是為了
        /// 一定要讓程序執(zhí)行finally語(yǔ)句塊中的releaseXXX方法,從而保證
        /// 能夠釋放鎖
        /// </summary>
        static void DisplayStudent()
        {
            try
            {
                _readAndWriteLock.AcquireReaderLock(Timeout.Infinite);
                demoList.ForEach(student
                  =>
                {
                    Console.WriteLine("當(dāng)前集合中學(xué)生為:{0},當(dāng)前讀操作線(xiàn)程為{1}", student.Name, Thread.CurrentThread.ManagedThreadId);
                });
            }
            catch (Exception)
            {
              
            }
            finally
            {
                _readAndWriteLock.ReleaseReaderLock();
            }
        }

    }

    internal class Student
    {
        public string Name { get; set; }
    }
復(fù)制代碼

運(yùn)行結(jié)果:

從例子可以看出有2個(gè)線(xiàn)程試圖嘗試爭(zhēng)取寫(xiě)鎖,,但是同時(shí)只有一個(gè)線(xiàn)程可以獲取到寫(xiě)鎖,,同時(shí)對(duì)于讀取集合的線(xiàn)程可以同時(shí)獲取多個(gè)讀鎖

6. 本章總結(jié)

由于本人上個(gè)月工作突然忙了起來(lái),快一個(gè)多月沒(méi)更新博客了,,希望大家可以見(jiàn)諒^^

本章介紹了線(xiàn)程同步的概念和一些關(guān)于同步非常重要的基本概念,,對(duì)于原子性的操作的認(rèn)識(shí)也格外重要,同時(shí)對(duì)于Volatile,Interlocked,lock,ReaderWriterLock 知識(shí)點(diǎn)做了相關(guān)介紹,,

相信大家對(duì)于線(xiàn)程同步有個(gè)初步的認(rèn)識(shí)和理解,,在寫(xiě)本篇博客時(shí),發(fā)現(xiàn)死鎖也是個(gè)很重要的知識(shí)點(diǎn),,關(guān)于死鎖我會(huì)單獨(dú)寫(xiě)篇文章來(lái)闡述,,謝謝大家的支持,!

7. 參考資料

CLR via c#

msdn

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,,不代表本站觀(guān)點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多