程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#各種同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEvent,

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

編輯:C#入門知識

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


看下組織結構:

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

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