前言

當(dāng)線程池的線程阻塞時(shí),線程池會(huì)創(chuàng)建額外的線程,而創(chuàng)建、銷毀和調(diào)度線程所需要相當(dāng)昂貴的內(nèi)存資源,另外,很多的開(kāi)發(fā)人員看見(jiàn)自己程序的線程沒(méi)有做任何有用的事情時(shí)習(xí)慣創(chuàng)建更多的線程,為了構(gòu)建可伸縮、響應(yīng)靈敏的程序,我們?cè)谇懊娼榻B了線程也瘋狂-----異步編程。

但是異步編程同樣也存在著很嚴(yán)重的問(wèn)題,如果兩個(gè)不同的線程訪問(wèn)相同的變量和數(shù)據(jù),按照我們異步函數(shù)的實(shí)現(xiàn)方式,不可能存在兩個(gè)線程同時(shí)訪問(wèn)相同的數(shù)據(jù),這個(gè)時(shí)候我們就需要線程同步。多個(gè)線程同時(shí)訪問(wèn)共享數(shù)據(jù)的時(shí),線程同步能防止數(shù)據(jù)損壞,之所以強(qiáng)調(diào)同時(shí)這個(gè)概念,因?yàn)榫€程同步本質(zhì)就是計(jì)時(shí)問(wèn)題。

異步和同步是相對(duì)的,同步就是順序執(zhí)行,執(zhí)行完一個(gè)再執(zhí)行下一個(gè),需要等待、協(xié)調(diào)運(yùn)行。異步就是彼此獨(dú)立,在等待某事件的過(guò)程中繼續(xù)做自己的事,不需要等待這一事件完成后再工作。線程就是實(shí)現(xiàn)異步的一個(gè)方式。異步是讓調(diào)用方法的主線程不需要同步等待另一線程的完成,從而可以讓主線程干其它的事情。

 

基元用戶模式和內(nèi)核模式構(gòu)造

基礎(chǔ)概念

基元:可以在代碼中使用的簡(jiǎn)單的構(gòu)造

用戶模式:通過(guò)特殊的CPU指令協(xié)調(diào)線程,操作系統(tǒng)永遠(yuǎn)檢測(cè)不到一個(gè)線程在基元用戶模式的構(gòu)造上阻塞。

內(nèi)核模式:由windows自身提供,在應(yīng)用程序的線程中調(diào)用由內(nèi)核實(shí)現(xiàn)的函數(shù)。

用戶模式構(gòu)造

易變構(gòu)造

 C#編譯器、JIT編譯器和CPU都會(huì)對(duì)代碼進(jìn)行優(yōu)化,它們盡量保證保留我們的意圖,但是從多線程的角度出發(fā),我們的意圖并不一定會(huì)得到保留,下面舉例說(shuō)明:

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫培訓(xùn)

 1  static void Main(string[] args) 2         { 3            Console.WriteLine("讓worker函數(shù)運(yùn)行5s后停止"); 4            var t = new Thread(Worker); 5             t.Start(); 6             Thread.Sleep(5000); 7             stop = true; 8  9             Console.ReadLine();10         }11 12         private static bool stop = false;13 14         private static void Worker(object obj)15         {16             int x = 0;17             while (!stop)18             {19                 x++;20             }21             Console.WriteLine("worker函數(shù)停止x={0}",x);22         }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫培訓(xùn)

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫培訓(xùn)

編譯器如果檢查到stop為false,就生成代碼來(lái)進(jìn)入一個(gè)無(wú)限循環(huán),并在循環(huán)中一直遞增x,所以優(yōu)化循環(huán)很快完成,但是編譯器只檢測(cè)stop一次,并不是每次都會(huì)檢測(cè)。

例子2---兩個(gè)線程同時(shí)訪問(wèn):

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫培訓(xùn)

 1 class test 2     { 3         private static int m_flag = 0; 4  5         private static int m_value = 0; 6  7         public static void Thread1(object obj) 8         { 9             m_value = 5;10             m_flag = 1;11 12         }13 14         public static void Thread2(object obj)15         {16             if (m_flag == 1)17                 Console.WriteLine("m_value = {0}", m_value);18         }19 20         //多核CPU機(jī)器才會(huì)出現(xiàn)線程同步問(wèn)題21         public void Exec()22         {23             var thread1 = new Thread(Thread1);24             var thread2 = new Thread(Thread2);25             thread1.Start();26             thread2.Start();27             Console.ReadLine();28         }29     }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫培訓(xùn)

程序在執(zhí)行的時(shí)候,編譯器必須將變量m_flag和m_value從RAM讀入CPU寄存器,RAM先傳遞m_value的值0,thread1把值變?yōu)?,但是thread2并不知道thread2仍然認(rèn)為值為0,這種問(wèn)題一般來(lái)說(shuō)發(fā)生在多核CPU的概率大一些,應(yīng)該CPU越多,多個(gè)線程同時(shí)訪問(wèn)資源的幾率就越大。

關(guān)鍵字volatile,作用禁止C#編譯器、JTP編譯器和CPU執(zhí)行的一些優(yōu)化,如果做用于變量后,將不允許字段緩存到CPU的寄存器中,確保字段的讀寫都在RAM中進(jìn)行。

 

互鎖構(gòu)造

System.Threading.Interlocked類中的每個(gè)方法都執(zhí)行一次原子的讀取以及寫入操作,調(diào)用某個(gè)Interlocked方法之前的任何變量寫入都在這個(gè)Interlocked方法調(diào)用之前執(zhí)行,而調(diào)用之后的任何變量讀取都在這個(gè)調(diào)用之后讀取。

Interlocked方法主要是對(duì)INT32變量進(jìn)行靜態(tài)操作Add、Decrement、Compare、Exchange、CompareChange等方法,也接受object、Double等類型的參數(shù)。

原子操作:是指不會(huì)被線程調(diào)度機(jī)制打斷的操作;這種操作一旦開(kāi)始,就一直運(yùn)行到結(jié)束,中間不會(huì)有任何 context switch (切換到另一個(gè)線程)。

代碼演示:

說(shuō)明:通過(guò)Interlocked的方法異步查詢幾個(gè)web服務(wù)器,并同時(shí)返回?cái)?shù)據(jù),且結(jié)果只執(zhí)行一次。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫培訓(xùn)

//上報(bào)狀態(tài)類型
    enum CoordinationStatus
    {
        Cancel,
        Timeout,
        AllDone
    }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫培訓(xùn)

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫培訓(xùn)

 1  class AsyncCoordinator 2     { 3         //AllBegun 內(nèi)部調(diào)用JustEnded來(lái)遞減它 4         private int _mOpCount = 1; 5  6         //0=false,1=true 7         private int _mStatusReported = 0; 8  9         private Action<CoordinationStatus> _mCallback;10 11         private Timer _mTimer;12 13         //發(fā)起一個(gè)操作之前調(diào)用14         public void AboutToBegin(int opsToAdd = 1)15         {16            Interlocked.Add(ref _mOpCount, opsToAdd);17         }18 19         //處理好一個(gè)操作的結(jié)果之后調(diào)用20         public void JustEnded()21         {22             if (Interlocked.Decrement(ref _mOpCount) == 0)23             {24                 ReportStatus(CoordinationStatus.AllDone);25             }        
26         }27 28         //該方法必須在發(fā)起所有操作后調(diào)用29         public void AllBegin(Action<CoordinationStatus> callback, int timeout = Timeout.Infinite)30         {31             _mCallback = callback;32             if (timeout != Timeout.Infinite)33             {34                 _mTimer = new Timer(TimeExpired, null, timeout, Timeout.Infinite);35                 JustEnded();36             }37         }38 39         private void TimeExpired(object o)40         {41             ReportStatus(CoordinationStatus.Timeout);42         }43 44         public void Cancel()45         {46             ReportStatus(CoordinationStatus.Cancel);47         }48 49         private void ReportStatus(CoordinationStatus status)50         {51             //如果狀態(tài)從未報(bào)告過(guò),就報(bào)告它,否則就忽略它,只調(diào)用一次52             if (Interlocked.Exchange(ref _mStatusReported, 1) == 0)53             {54                 _mCallback(status);55             }         
56         }57     }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫培訓(xùn)

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫培訓(xùn)

 1 class MultiWebRequest 2     { 3         //輔助類 用于協(xié)調(diào)所有的異步操作 4         private AsyncCoordinator _mac = new AsyncCoordinator(); 5  6         protected Dictionary<string,object> _mServers = new Dictionary<string, object> 7         { 8             {"http://www.baidu.com",null},{"http://www.Microsoft.com",null},{"http://www.cctv.com",null}, 9             {"http://www.souhu.com",null},{"http://www.sina.com",null},{"http://www.tencent.com",null},10             {"http://www.youku.com",null}11         };12 13         private Stopwatch sp;14         public MultiWebRequest(int timeout = Timeout.Infinite)15         {16             sp = new Stopwatch();17             sp.Start();18             //通過(guò)異步方式一次性發(fā)起請(qǐng)求19             var httpclient = new HttpClient();20 21             foreach (var server in _mServers.Keys)22             {23                 _mac.AboutToBegin(1);24 25                 httpclient.GetByteArrayAsync(server).ContinueWith(task => ComputeResult(server, task));26             }27             _mac.AllBegin(AllDone,timeout);28             Console.WriteLine("");29         }30 31         private void ComputeResult(string server, Task<Byte[]> task)32         {33             object result;34             if (task.Exception != null)35             {36                 result = task.Exception.InnerException;37             }38             else39             {40                 //線程池處理IO41                 result = task.Result.Length;42             }43 44             //保存返回結(jié)果的長(zhǎng)度45             _mServers[server] = result;46 47             _mac.JustEnded();48         }49 50         public void Cancel()51         {52             _mac.Cancel();53         }54 55         private void AllDone(CoordinationStatus status)56         {57             sp.Stop();58             Console.WriteLine("響應(yīng)耗時(shí)總計(jì){0}",sp.Elapsed);59             switch (status)60             {61                 case CoordinationStatus.Cancel:62                     Console.WriteLine("操作取消");63                     break;64                 case CoordinationStatus.AllDone:65                     Console.WriteLine("操作完成,完成的結(jié)果如下");66                     foreach (var server in _mServers)67                     {68                         Console.WriteLine("{0}",server.Key);69                         object result = server.Value;70                         if (result is Exception)71                         {72                             Console.WriteLine("錯(cuò)誤原因{0}",result.GetType().Name);73                         }74                         else75                         {76                             Console.WriteLine("返回字節(jié)數(shù)為:{0}",result);77                         }78                     }79                     break;80                 case CoordinationStatus.Timeout:81                     Console.WriteLine("操作超時(shí)");82                     break;83                 default:84                     throw new ArgumentOutOfRangeException("status", status, null);85             }86         }87     }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫培訓(xùn)

非常建議大家參考一下以上代碼,我在對(duì)服務(wù)器進(jìn)行訪問(wèn)時(shí),也會(huì)常常參考這個(gè)模型。

簡(jiǎn)單的自旋鎖

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫培訓(xùn)

 1 class SomeResource 2     { 3          private SimpleSpinLock s1 = new SimpleSpinLock(); 4  5         public void AccessResource() 6         { 7             s1.Enter(); 8             //一次是有一個(gè)線程才能進(jìn)入訪問(wèn) 9             s1.Leave();10 11         }12     }13 14     class SimpleSpinLock15     {16         private int _mResourceInUse;17 18         public void Enter()19         {20             while (true)21             {22                 if(Interlocked.Exchange(ref _mResourceInUse,1)==0)23                     return;24             }25         }26 27         public void Leave()28         {29             Volatile.Write(ref _mResourceInUse,1);30         }31     }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫培訓(xùn)

這就是一個(gè)線程同步鎖的簡(jiǎn)單實(shí)現(xiàn),這種鎖的最大問(wèn)題在于,存在競(jìng)爭(zhēng)的情況下會(huì)造成線程的“自旋”,這會(huì)浪費(fèi)CPU的寶貴時(shí)間,組織CPU做更多的工作,因此,這種自旋鎖應(yīng)該用于保護(hù)那些執(zhí)行的非??斓拇a。

 

下篇我們將繼續(xù)講解線程同步,內(nèi)核模式構(gòu)造和混合線程同步構(gòu)造,希望這些內(nèi)容可以幫助到大家一起成長(zhǎng),如果發(fā)現(xiàn)博客有什么錯(cuò)誤請(qǐng)及時(shí)提出寶貴意見(jiàn),以免造成誤導(dǎo)!

http://www.cnblogs.com/dongqinnanren/p/6370726.html