ManualResetEvent&ManualResetEventSlim

Shapley發表於2024-10-19

ManualResetEvent

ManualResetEvent有三個重要的方法,分別為:waiteone(),set(),reset(),其含義如下:

1.WaitOne()即等待訊號發出,即可往下執行。

2.set()發出訊號,讓執行緒方法繼續往下執行,並允許其他執行緒(如有)一併往下執行。

3.reset()重新初始化(即:去掉票據)變為ManualResetEvent(false)形式。

官方示例如下:

internal class Program
{
    private static ManualResetEvent mre = new ManualResetEvent(false);
    static void Main()
    {
        Console.WriteLine("\n初始化3個執行緒,並在遇到waitone時阻塞執行:\n");
        for (int i = 0; i <= 2; i++)
        {
            Thread t = new Thread(ThreadProc);
            t.Name = "Thread_" + i;
            t.Start();
        }

        Thread.Sleep(500);
        Console.WriteLine("\n三個執行緒已經啟動,請按Enter鍵呼叫Set()方法來釋放3個阻塞執行緒");
        Console.ReadLine();

        mre.Set();

        Thread.Sleep(500);
        Console.WriteLine("\n當這個ManualResetEvent活得訊號,3個執行緒都會從WaitOne()方法內返回,並繼續執行(如果有新執行緒被建立)不會被阻塞,可按Enter鍵進行觀察:\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("\n按Enter鍵呼叫 Reset()方法(設定為無訊號狀態),重新初始化一個執行緒執行,並在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("\n按Enter鍵呼叫 Set() 方法,然後結束演示.");
        Console.ReadLine();

        mre.Set();
        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.");
    }
}

執行結果:

ManualResetEventSlim:

ManualResetEventSlim是ManualResetEvent的簡化版或者叫最佳化版,其主要原理為採用了自旋方式來提高效能,適用於短期內等待的情況,效能比後者要好很多。適用於一次寫入,多次讀取時候的執行緒同步場景。

官方示例程式碼:

 internal class Program
 {
     static void Main(string[] args)
     {
         MRES_SetWaitReset();
         Console.ReadKey();
     }
     static void MRES_SetWaitReset()
     {
         ManualResetEventSlim mres1 = new ManualResetEventSlim(false);
         ManualResetEventSlim mres2 = new ManualResetEventSlim(false);
         ManualResetEventSlim mres3 = new ManualResetEventSlim(true); 

         var observer = Task.Factory.StartNew(() =>
         {
             mres1.Wait();
             Console.WriteLine("mres1!阻塞結束繼續執行");
             Console.WriteLine("mres3即將被重置");
             mres3.Reset(); 
             Console.WriteLine("mres2即將獲取到票據");
             mres2.Set();
         });

         Console.WriteLine("主執行緒: mres3.IsSet = {0} (should be true)", mres3.IsSet);
         Console.WriteLine("主執行緒:mres1即將獲取訊號");
         mres1.Set();
         Console.WriteLine("主執行緒:mres2即將阻塞");
         mres2.Wait();
         Console.WriteLine("主執行緒:mres2已獲取訊號!");
         Console.WriteLine("主執行緒: mres3.IsSet = {0} (should be false)", mres3.IsSet);
observer.Wait();
// make sure that this has fully completed mres1.Dispose(); mres2.Dispose(); mres3.Dispose(); } }

執行結果: