C#各種同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEvent

来源:http://www.cnblogs.com/gujf2016/archive/2016/12/31/6238948.html
-Advertisement-
Play Games

看下組織結構: System.Object System.MarshalByRefObject System.Threading.WaitHandle System.Threading.Mutex System.Threading.Semaphore System.Threading.EventWa ...


看下組織結構:

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

        System.Threading.AutoResetEvent

 

System.Object

  System.Threading.Interlocked
  System.Threading.Monitor

  System.Threading.ReaderWriterLock

 

1, lock 關鍵字其實就是對 Monitor 類的 Enter()和 Exit()方法的封裝。通過 try......catch......finally 語句塊確保在 lock 語句塊結束後執行 Monitor.Exit()方法,釋放互斥鎖。下麵2段代碼等價:

     

lock(locker)
{
  //do something
}
View Code

 

Monitor.Enter(locker);
try
{
    // do something
}
finally
{
    Monitor.Exit(locker);
}
View Code

 

2,

Monitor類通過向單個線程授予對象鎖來控制對對象的訪問。對象鎖提供限制訪問臨界區的能力。當一個線程擁有對象的鎖時,其他任何線程都不能獲取該鎖。還可以使用 Monitor 來確保不會允許其他任何線程訪問正在由鎖的所有者執行的應用程式代碼節,除非另一個線程正在使用其他的鎖定對象執行該代碼。通過對 lock 關鍵字的分析我們知道,lock 就是對 Monitor 的 Enter 和 Exit 的一個封裝,而且使用起來更簡潔,因此 Monitor 類的 Enter()和 Exit()方法的組合使用可以用 lock 關鍵字替代。 
   Monitor 類的常用方法: 
        TryEnter(): 
            能夠有效的解決長期死等的問題,如果在一個併發經常發生,而且持續時間長的環境中使用 TryEnter,可以有效防止死鎖或者長時間的等待。比如我們可以設置一個等待時間 bool gotLock = Monitor.TryEnter(myobject,1000),讓當前線程在等待 1000 秒後根據返回的 bool 值來決定是否繼續下麵的操作。

        Wait() :

            釋放對象上的鎖以便允許其他線程鎖定和訪問該對象。在其他線程訪問對象時,調用線程將等待。脈衝信號用於通知等待線程有關對象狀態的更改。 
        Pulse(): 
        PulseAll(): 
            向一個或多個等待線程發送信號。該信號通知等待線程鎖定對象的狀態已更改,並且鎖的所有者準備釋放該鎖。等待線程被放置在對象的就緒隊列中以便它可以最後接收對象鎖。一旦線程擁有了鎖,它就可以檢查對象的新狀態以查看是否達到所需狀態。註意:Pulse、PulseAll 和 Wait 方法必須從同步的代碼塊內調用。

       static object locker = new object();
        static bool isHave = false;

        static void Produce()
        {
            lock (locker)
            {
                while (true)
                {
                    //如果已有產品,則等待消費完成
                    if (isHave)
                        Monitor.Wait(locker);
                    Console.WriteLine("生產一個");
                    Thread.Sleep(1000);
                    isHave = true;
                    Monitor.Pulse(locker);
                }
            }
        }
        static void Consume()
        {
            lock (locker)
            {
                while (true)
                {
                    //如果沒有產品,則等待生產完成
                    if (!isHave)
                        Monitor.Wait(locker);
                    Console.WriteLine("消費一個");
                    Thread.Sleep(500);
                    isHave = false;
                    Monitor.Pulse(locker);
                }
            }
        }
View Code

在main函數中調用:

            new Thread(Produce).Start();
            new Thread(Consume).Start();
View Code

 

3, Mutex互斥體

public class Test
    {
        // Create a new Mutex. The creating thread does not own the
        // Mutex.
        private static Mutex mut = new Mutex();

        public static void MyThreadProc()
        {
            for (int i = 0; i < 2; i++)
            {
                UseResource();
            }
        }

        // This method represents a resource that must be synchronized
        // so that only one thread at a time can enter.
        private static void UseResource()
        {
            // Wait until it is safe to enter.
            mut.WaitOne();

            Console.WriteLine("{0} has entered the protected area",
                Thread.CurrentThread.Name);

            // Place code to access non-reentrant resources here.

            // Simulate some work.
            Thread.Sleep(500);

            Console.WriteLine("{0} is leaving the protected area\r\n",
                Thread.CurrentThread.Name);

            // Release the Mutex.
            mut.ReleaseMutex();
        }
    }
View Code
Test test = new Test();
            for (int i = 0; i < 3; i++)
            {
                Thread myThread = new Thread(new ThreadStart(Test.MyThreadProc));
                myThread.Name = String.Format("Thread{0}", i + 1);
                myThread.Start();
            }
View Code

 mutex還可以判斷系統是否已經有一個進程存在。

 

4,Semaphore 信號量

static Semaphore sph = new Semaphore(0, 3);
        static void TProc()
        {
            while (true)
            {
                if (sph.WaitOne(500, false))
                {
                    try
                    {
                        Console.WriteLine("thread" + Thread.CurrentThread.Name + ":enter");
                        Thread.Sleep(1000);
                    }
                    finally
                    {
                        sph.Release();
                        Console.WriteLine("thread" + Thread.CurrentThread.Name + ":exit");
                    }
                }
                else
                {
                    Console.WriteLine("thread" + Thread.CurrentThread.Name + ":time out");
                }
            }
        }
View Code
Thread t = null;
            for (int i = 0; i < 2; i++)
            {
                t = new Thread(TProc);
                t.Name = i.ToString();
                t.Start();
            }
            Console.WriteLine("main sleep 4s");
            Thread.Sleep(4000);
            sph.Release(2);
View Code

 

5,Interlocker類為多個線程共用的變數提供原子操作,它是一個靜態類,主要的成員方法如下:
Add:以原子操作的形式,添加兩個整數並用兩者的和替換第一個整數。
Exchange:以原子操作的形式將變數設置為指定的值,並返回先前值
CompareExchange:比較兩個值是否相等,如果相等,則替換其中一個值
Equals:確定兩個Object 實例是否相等
Increment:以原子操作的形式遞增指定變數的值並存儲結果
Decrement:以原子操作的形式遞減指定變數的值並存儲結果
Read:返回一個以原子操作形式載入的 64 位值

Interlocked.CompareExchange(ref obj, new object(), null);
View Code

 

6, ReaderWriterLock

static ReaderWriterLock rwLock = new ReaderWriterLock();
static object locker = new object();
static void Main(string[] args)
{
    Thread t = null;
    for(int i = 0; i < 2;i++)
    {
        t = newThread(Writer);
        t.Name =i.ToString();
        t.Start();
    }
    for(int i = 0; i<3;i++)
    {
        t = newThread(Reader);
        t.Name =i.ToString();
        t.Start();
    }
    Console.ReadLine();
}
static void Writer()
{
    while(true)
    {
        try
        {
            rwLock.AcquireWriterLock(3000);
            Console.WriteLine("writer:"+ Thread.CurrentThread.Name + " is enter" + "WriterSeqNum:" +rwLock.WriterSeqNum.ToString());
            try
            {
                Thread.Sleep(5000);
            }
            finally
            {
                rwLock.ReleaseWriterLock();
                Console.WriteLine("writer:"+ Thread.CurrentThread.Name + " is exit");
            }
        }
        catch(ApplicationException)
        {
            Console.WriteLine("writer:"+ Thread.CurrentThread.Name + " wait time out");
        }
    }
}
static void Reader()
{
    while (true)
    {
        rwLock.AcquireReaderLock(-1);
        Console.WriteLine("reader:"+ Thread.CurrentThread.Name + " is enter" + "WriterSeqNum:" +rwLock.WriterSeqNum.ToString());
        try
        {
            Thread.Sleep(3000);
        }
        finally
        {
            Console.WriteLine("reader:"+ Thread.CurrentThread.Name + " is exit");
            rwLock.ReleaseReaderLock();
        }
    }
}
View Code

 

7,AutoResetEvent 自動重置事件

在構造事件對象時需要指定initialState參數是True還是False,以指示事件的初始狀態是有信號還是無信號

當一個自動重置事件得到信號時,等待該事件的線程中只有一個線程變為可調度線程,當手動重置對象得到信號時,等待該事件的所有線程均變為可調度線程

 

class Example
{
    private static AutoResetEvent event_1 = new AutoResetEvent(true);
    private static AutoResetEvent event_2 = new AutoResetEvent(false);

    static void Main()
    {
        Console.WriteLine("Press Enter to create three threads and start them.\r\n" +
                          "The threads wait on AutoResetEvent #1, which was created\r\n" +
                          "in the signaled state, so the first thread is released.\r\n" +
                          "This puts AutoResetEvent #1 into the unsignaled state.");
        Console.ReadLine();

        for (int i = 1; i < 4; i++)
        {
            Thread t = new Thread(ThreadProc);
            t.Name = "Thread_" + i;
            t.Start();
        }
        Thread.Sleep(250);

        for (int i = 0; i < 2; i++)
        {
            Console.WriteLine("Press Enter to release another thread.");
            Console.ReadLine();
            event_1.Set();
            Thread.Sleep(250);
        }

        Console.WriteLine("\r\nAll threads are now waiting on AutoResetEvent #2.");
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine("Press Enter to release a thread.");
            Console.ReadLine();
            event_2.Set();
            Thread.Sleep(250);
        }

        // Visual Studio: Uncomment the following line.
        //Console.Readline();
    }

    static void ThreadProc()
    {
        string name = Thread.CurrentThread.Name;

        Console.WriteLine("{0} waits on AutoResetEvent #1.", name);
        event_1.WaitOne();
        Console.WriteLine("{0} is released from AutoResetEvent #1.", name);

        Console.WriteLine("{0} waits on AutoResetEvent #2.", name);
        event_2.WaitOne();
        Console.WriteLine("{0} is released from AutoResetEvent #2.", name);

        Console.WriteLine("{0} ends.", name);
    }
}
View Code

 

8, ManualResetEvent 手動重置事件

public class Example
{
    // mre is used to block and release threads manually. It is
    // created in the unsignaled state.
    private static ManualResetEvent mre = new ManualResetEvent(false);

    static void Main()
    {
        Console.WriteLine("\nStart 3 named threads that block on a ManualResetEvent:\n");

        for(int i = 0; i <= 2; i++)
        {
            Thread t = new Thread(ThreadProc);
            t.Name = "Thread_" + i;
            t.Start();
        }

        Thread.Sleep(500);
        Console.WriteLine("\nWhen all three threads have started, press Enter to call Set()" +
                          "\nto release all the threads.\n");
        Console.ReadLine();

        mre.Set();

        Thread.Sleep(500);
        Console.WriteLine("\nWhen a ManualResetEvent is signaled, threads that call WaitOne()" +
                          "\ndo not block. Press Enter to show this.\n");
        Console.ReadLine();

        for(int i = 3; i <= 4; i++)
        {
            Thread t = new Thread(ThreadProc);
            t.Name = "Thread_" + i;
            t.Start();
        }

        Thread.Sleep(500);
        Console.WriteLine("\nPress Enter to call Reset(), so that threads once again block" +
                          "\nwhen they call WaitOne().\n");
        Console.ReadLine();

        mre.Reset();

        // Start a thread that waits on the ManualResetEvent.
        Thread t5 = new Thread(ThreadProc);
        t5.Name = "Thread_5";
        t5.Start();

        Thread.Sleep(500);
        Console.WriteLine("\nPress Enter to call Set() and conclude the demo.");
        Console.ReadLine();

        mre.Set();

        // If you run this example in Visual Studio, uncomment the following line:
        //Console.ReadLine();
    }


    private static void ThreadProc()
    {
        string name = Thread.CurrentThread.Name;

        Console.WriteLine(name + " starts and calls mre.WaitOne()");

        mre.WaitOne();

        Console.WriteLine(name + " ends.");
    }
}
View Code

 

9, .NET 在一些集合類,如 Queue、ArrayList、HashTable 和 Stack,已經提供了一個供 lock 使用的對象 SyncRoot。

Queue q = new Queue(); 
                lock (q.SyncRoot) 
                { 
                    foreach (object item in q) 
                    { 
                        //do something 
                    } 
                }  
View Code

 

參考資料:http://blog.csdn.net/zzy7075/article/details/29842165


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 原文地址:http://blog.csdn.net/ausboyue/article/details/52775281 Linux SSH命令錯誤:ECDSA host key "ip地址" for has changed and you have requested strict checking... ...
  • 原文地址:http://www.cnblogs.com/cocowool/archive/2012/07/05/2578487.html YUM代理設置 編輯/etc/yum.conf,在最後加入 # Proxy proxy=http://username:password@proxy_ip:por... ...
  • 1、Bin 目錄 用來存放編譯的結果,bin是二進位binary的英文縮寫,因為最初C編譯的程式文件都是二進位文件,它有Debug和Release兩個版本,分別對應的文件夾為bin/Debug和bin/Release,這個文件夾是預設的輸出路徑,我們可以通過:項目屬性—>配置屬性—>輸出路徑來修改。 ...
  • 1、在程式最前面加: #define _CRT_SECURE_NO_DEPRECATE 2、在程式最前面加: #pragma warning(disable:4996) 3、把scanf改為scanf_s; 4、無需在程式最前面加那行代碼,只需在新建項目時取消勾選“SDL檢查”即可; 5、若項目已建 ...
  • 一、下麵是在創建一個新的項目是我最常用的,現在對他們一一做一個詳細的介紹: 1、Win32控制台應用程式我平時編寫小的C/C++程式都用它,它應該是用的最多的。 2、名稱和解決方案名稱的區別:名稱是項目的名稱,一個解決方案中可以包含多個項目,所以解決方案名稱包含項目名稱。 3、新建Git存儲庫(G) ...
  • 應用場景 應用場景 angular2(下文中標註位NG2)項目和.net mvc項目分別開發,前期採用跨域訪問進行並行開發,後期只需要將NG2項目的生產版本合併到.net項目。 NG2項目概述 NG2項目概述 ng2項目採用的是angular-cli搭建的框架。 使用type script、rxjs ...
  • 在我上篇隨筆《在DevExpress程式中使用Winform分頁控制項直接錄入數據並保存》中介紹了在GridView以及在其封裝的分頁控制項上做數據的直接錄入的處理,介紹情況下數據的保存和校驗等操作,不過還沒有涉及到數據列表選擇的這種方式,而這種在項目應用也是比較廣泛的一種輸入方式。本篇隨筆繼續探討在G... ...
  • //我的C#是跟著猛哥(劉鐵猛)(算是我的正式老師)《C#語言入門詳解》學習的,微信上猛哥也給我講解了一些不懂得地方,對於我來說簡直是一筆巨額財富,難得良師! 這次與大家一起學習C#中的值參數 傳值參數(也叫值參數) 值參數 (value parameter) 什麼是值參數? 這是從《C#語言規範5 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...