從0自學C#12--線程同步解決方案匯總以及優缺點

來源:互聯網
上載者:User
首先,肯定的一點:Microsoft的Framework Class Library(FCL)保證了所有靜態方法都是安全執行緒的。

FCL不保證執行個體方法是安全執行緒的。因為假如全部添加鎖定,會造成效能的巨大損失。另外,假如每個執行個體方法都需要擷取和釋放一個鎖,事實上會造成最終在任何給定的時刻,你的應用程式只有一個線程在運行,這對效能的影響顯而易見。

下面介紹基元線程同步構造。

基元:是指可以在代碼中使用的最簡單的構造。有兩種基元構造:使用者模式(user-mode)和核心模式(kernel-mode)。

使用者模式

使用了特殊的CPU指令來協調線程。

技術:volatile關鍵字、Interlocked類(互鎖)、spinlock(自旋鎖)

常見鎖①:volatile 關鍵字指示一個欄位可以由多個同時執行的線程修改。 聲明為 volatile 的欄位不受編譯器最佳化(假定由單個線程訪問)的限制。 這樣可以確保該欄位在任何時間呈現的都是最新的值。

Interlocked類: 為多個線程共用的變數提供原子操作。。所謂原子操作是指不會被線程調度機制打斷的操作;這種操作一旦開始,就一直運行到結束,中間不會有任何 context switch (切換到另一個線程)。

常見鎖②:SpinLock 結構是一個低層級的互斥同步基元,它在等待擷取鎖時進行旋轉。在多核電腦上,當等待時間預計較短且極少出現爭用情況時,SpinLock 的效能將高於其他類型的鎖。即使 SpinLock 未擷取鎖,它也會產生線程的時間片。 它這樣做是為了避免線程優先順序別反轉,並使記憶體回收行程能夠繼續執行。 在使用 SpinLock 時,請確保任何線程持有鎖的時間不會超過一個非常短的時間段,並確保任何線程在持有鎖時不會阻塞。

優點:

應盡量使用基元使用者模式構造,它們的速度要顯著快於核心模式的構造。

協調線程的在硬體中發生的(所以才這麼快)。

但是Microsoft Windows作業系統永遠檢測不到一個線程在基元使用者模式的構造上阻塞了。

由於在使用者模式的基元構造上阻塞的線程池永遠不認為已堵塞,所以線程池不會建立新線程來替換這種臨時的線程。

這些CPU指令只阻塞線程相當短的時間。

缺點:

只有Windows作業系統核心才能停止一個線程的運行(防止它浪費CPU的時間)。

在使用者模式中啟動並執行線程可能被系統搶佔,但線程會以最快的速度再次調度。

想要取得資源但暫時取不到的線程會一直在使用者模式中“自旋”,這可能浪費大量的CPU時間。線程一直在一個CPU上運行,我們稱為“活鎖”(livelock)。

執行個體:

using System;using System.Threading;public class Worker{    // This method is called when the thread is started.    public void DoWork()    {        while (!_shouldStop)        {            Console.WriteLine("Worker thread: working...");        }        Console.WriteLine("Worker thread: terminating gracefully.");    }    public void RequestStop()    {        _shouldStop = true;    }    // Keyword volatile is used as a hint to the compiler that this data    // member is accessed by multiple threads.    private volatile bool _shouldStop;}public class WorkerThreadExample{    static void Main()    {        // Create the worker thread object. This does not start the thread.        Worker workerObject = new Worker();        Thread workerThread = new Thread(workerObject.DoWork);        // Start the worker thread.        workerThread.Start();        Console.WriteLine("Main thread: starting worker thread...");        // Loop until the worker thread activates.        while (!workerThread.IsAlive) ;        // Put the main thread to sleep for 1 millisecond to        // allow the worker thread to do some work.        Thread.Sleep(1);        // Request that the worker thread stop itself.        workerObject.RequestStop();        // Use the Thread.Join method to block the current thread         // until the object's thread terminates.        workerThread.Join();        Console.WriteLine("Main thread: worker thread has terminated.");    }    // Sample output:    // Main thread: starting worker thread...    // Worker thread: working...    // Worker thread: working...    // Worker thread: working...    // Worker thread: working...    // Worker thread: working...    // Worker thread: working...    // Worker thread: terminating gracefully.    // Main thread: worker thread has terminated.}

核心模式

由Windows作業系統自身提供的。它們要求在應用程式的線程中調用有作業系統核心實現的函數。

技術:EventWaitHandle(事件)、Semaphore(訊號量)、Mutex(互斥體)

System.Object  System.MarshalByRefObject    System.Threading.WaitHandle      System.Threading.EventWaitHandle      System.Threading.Mutex      System.Threading.Semaphore

常見鎖③:Mutex 類是 Win32 構造的封裝,它可以跨應用程式定義域邊界進行封送處理,可用於多個等待,並且可用於同步不同進程中的線程。

優點:

線程通過核心模式的構造擷取其他線程擁有的資源時,Windows會阻塞線程以避免它浪費CPU時間。當資源變得可用時,Windows會恢複線程,允許它訪問資源。它不會佔著一個CPU“自旋”。

可實現本機和託管線程相互之間的同步。

可同步在同一台機器的不同進程中啟動並執行線程。

可應用安全性設定,防止未經授權的賬戶訪問它們。

線程可一直阻塞,直到及合作的所有核心模式構造都可用,或者直到集合中的任何核心模式構造可用。

在核心模式的構造上阻塞的線程可指定逾時值:指定時間內訪問不到希望的資源,線程就可以解除阻塞並執行其他任務。

缺點:

將線程從使用者模式切換為核心模式(或者相反)會招致巨大的效能損失,這正是為什麼要避免使用核心構造的原因。另外,線程一直阻塞,會導致“死結“(deadlock)。

死結總是由於活鎖,因為活鎖即浪費CPU時間,有浪費記憶體(線程棧等),而死結只浪費記憶體。

混合構造

兼具上面兩者的長處。在沒有競爭的情況下,快而且不會阻塞(就像使用者模式)。在有競爭的情況,希望它被作業系統核心阻塞。

技術:ManualResetEventSlim類、SemaphoreSlim類、Monitor類、Lock類、ReaderWriterLockSlim類、CountdownEvent類、Barrier類、雙檢鎖.

常見鎖④:Monitor 通常更為可取,因為監視器是專門為 .NET Framework 而設計的,因而它比Mutex可以更好地利用資源。儘管 mutex 比監視器更為強大,但是相對於 Monitor 類,它所需要的互操作轉換更消耗計算資源。

常見鎖⑤:使用 lock (C#) 或 SyncLock (Visual Basic) 關鍵字是Monitor的封裝。通常比直接使用 Monitor 類更可取,一方面是因為 lock 或 SyncLock 更簡潔,另一方面是因為lock 或 SyncLock 確保了即使受保護的代碼引發異常,也可以釋放基礎監視器。

常見鎖⑥:ReaderWriterLock 鎖,在某些情況下,可能希望只在寫入資料時鎖定資源,在不更新資料時允許多個用戶端同時讀取資料。ReaderWriterLock 類線上程修改資源時將強制其獨佔訪問資源,但在讀取資源時則允許非獨佔訪問。 ReaderWriter 鎖可用於代替排它鎖。使用排它鎖時,即使其他線程不需要更新資料,也會讓這些線程等待。

雙檢鎖

常見鎖⑦:雙重檢查鎖定模式(也被稱為”雙重檢查加鎖最佳化”,”鎖暗示”(Lock hint)) 是一種軟體設計模式用來減少並發系統中競爭和同步的開銷。

雙重檢查鎖定模式首先驗證鎖定條件(第一次檢查),只有通過鎖定條件驗證才真正的進行加鎖邏輯並再次驗證條件(第二次檢查)。

它通常用於減少加鎖開銷,尤其是為多線程環境中的單例模式實現“惰性初始化”。惰性初始化的意思是直到第一次訪問時才初始化它的值。

public sealed class Singleton    {        private static volatile Singleton instance = null;        private static object syncRoot = new Object();        private static int count = 100;        private Singleton() { }        public static Singleton Instance        {            get            {                if (instance == null)                {                    lock (syncRoot)                    {                        if (instance == null)                            instance = new Singleton();                    }                }                return instance;            }        }        public static Singleton GetSingleton()        {            if (instance == null)            {                lock (syncRoot)                {                    if (instance == null)                    {                        instance = new Singleton();                    }                                        }            }            return instance;        }        public override string ToString()        {            lock (syncRoot)            {                int buf = --count;                Thread.Sleep(20);                return buf + "_" + count.ToString();            }        }    }static void Main(string[] args)        {            Task<string>[] tasks = new Task<string>[10];            Stopwatch watch = new Stopwatch();            object syncRoot = new Object();            watch.Start();            for (int i = 0; i < tasks.Length; i++)            {                tasks[i] = Task.Factory.StartNew<string>(() =>(Singleton.GetSingleton().ToString()));                                }            Task.WaitAll(tasks, 5000);//設定逾時5s            watch.Stop();            Console.WriteLine("Tasks running need " + watch.ElapsedMilliseconds + " ms." + "\n");            for (int i = 0; i < tasks.Length; i++)            {                //逾時處理                if (tasks[i].Status != TaskStatus.RanToCompletion)                {                    Console.WriteLine("Task {0} Error!", i + 1);                }                else                {                    //save result                    Console.WriteLine("Tick ID is " + tasks[i].Result);                    Console.WriteLine();                }            }            for (int i = 0; i < 3; i++)            {                Console.WriteLine("Main thread do work!");                Thread.Sleep(200);            }            Console.ReadKey();        }

輸出:

Tasks running need 298 ms.Tick ID is 96_96Tick ID is 99_99Tick ID is 97_97Tick ID is 98_98Tick ID is 95_95Tick ID is 94_94Tick ID is 93_93Tick ID is 92_92Tick ID is 91_91Tick ID is 90_90Main thread do work!Main thread do work!Main thread do work!

以上就是從0自學C#12--線程同步解決方案匯總以及優缺點的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.