AutoResetEvent 、 ManualResetEvent實現同步

 

線程相關的概念
1.線程同步
ManualResetEvent
Set()方法將狀態設置爲有信號
Reset()將其設置爲無信號
WaitOne()將阻塞到其有信號爲止,若調用WaitOne的時刻就是有信號的,將不會阻塞函數

AutoResetEvent
與ManualResetEvent的區別是,AutoResetEvent.WaitOne()會自動改變事件對象的狀態,即AutoResetEvent.WaitOne()每執行一次,事件的狀態就改變一次。有信號-->無信號;無信號-->有信號spa

 

C#讀寫線程(用AutoResetEvent實現同步)線程

1. AutoResetEvent簡介code

 

通知正在等待的線程已發生事件。沒法繼承此類。對象

 

經常使用方法簡介:blog

AutoResetEvent(bool initialState):構造函數,用一個指示是否將初始狀態設置爲終止的布爾值初始化該類的新實例。
    false:無信號,子線程的WaitOne方法不會被自動調用
    true:有信號,子線程的WaitOne方法會被自動調用繼承

public bool Reset ():將事件狀態設置爲非終止狀態,致使線程阻止;若是該操做成功,則返回true;不然,返回false。 
public bool Set ():將事件狀態設置爲終止狀態,容許一個或多個等待線程繼續;若是該操做成功,則返回true;不然,返回false。
    對於具備 EventResetMode.AutoReset(包括 AutoResetEvent)的 EventWaitHandle,Set 方法釋放單個線程。若是沒有等待線程,等待句柄將一直保持終止狀態,直到某個線程嘗試等待它,或者直到它的 Reset 方法被調用。事件


    對於具備 EventResetMode.ManualReset(包括 ManualResetEvent)的 EventWaitHandle,調用Set 方法將使等待句柄一直保持終止狀態,直到它的 Reset 方法被調用。同步

 

WaitOne方法
       當在派生類中重寫時,阻止當前線程,直到當前的 WaitHandle 收到信號。string

WaitHandle.WaitOne () 當在派生類中重寫時,阻止當前線程,直到當前的 WaitHandle 收到信號。 由.NET Compact Framework 支持。 
WaitHandle.WaitOne(Int32, Boolean)  在派生類中被重寫時,阻止當前線程,直到當前的WaitHandle 收到信號,使用 32 位有符號整數度量時間間隔並指定是否在等待以前退出同步域。由 .NET Compact Framework 支持。 
WaitHandle.WaitOne(TimeSpan, Boolean)  在派生類中被重寫時,阻止當前線程,直到當前實例收到信號,使用 TimeSpan 度量時間間隔並指定是否在等待以前退出同步域。
2. 讀寫者線程例子

 

本例子中,主線程做爲寫線程,要對某個數據(本例中是個變量)賦值(即寫動做),而讀線程則等待寫線程每次寫完數據發出通知,待讀線程收到通知後,將數據讀出並顯示。

using System;
using System.Collections.Generic;
using System.Text;

using System.Threading;

namespace TestAutoResetEvent
{
    /// 
    /// 讀寫者線程
    /// 主線程寫,子線程讀,且只有將數據寫入後,讀線程才能將其讀出
    /// 
    class Program
    {
        //寫線程將數據寫入myData
        static int myData = 100;

        //讀寫次數
        const int readWriteCount = 10;

        //false:初始時沒有信號
        static AutoResetEvent autoResetEvent = new AutoResetEvent(false);

        static void Main(string[] args)
        {
            //開啓一個讀線程(子線程)
            Thread readerThread = new Thread(new ThreadStart(ReadThreadProc));
            readerThread.Name = "ReaderThread";
            readerThread.Start();

            for (int i = 1; i <= readWriteCount; i++)
            {
                Console.WriteLine("MainThread writing : {0}", i);

                //主(寫)線程將數據寫入
                myData = i;

                //主(寫)線程發信號,說明值已寫過了
                //即通知正在等待的線程有事件發生
                autoResetEvent.Set();

                Thread.Sleep(0);
            }

            //終止線程
            readerThread.Abort();
        }

        static void ReadThreadProc()
        {
            while (true)
            {
                //在數據被寫入前,讀線程等待(其實是等待寫線程發出數據寫完的信號)
                autoResetEvent.WaitOne();
                Console.WriteLine("{0} reading : {1}", Thread.CurrentThread.Name, myData);
            }
        }
    }
}

 

運行結果以下:

 

 

 

 

由運行結果能夠看出,寫線程寫入的數據有丟失,主要緣由是寫線程沒有給讀線程留足夠的時間去進行讀操做。

 

3. 對1進行修改


將主線程睡眠時間改成非0值,觀察運行結果。

using System;
using System.Collections.Generic;
using System.Text;

using System.Threading;

namespace TestAutoResetEvent
{
    /// 
    /// 讀寫者線程
    /// 主線程寫,子線程讀,且只有將數據寫入後,讀線程才能將其讀出
    /// 
    class Program
    {
        //寫線程將數據寫入myData
        static int myData = 100;

        //讀寫次數
        const int readWriteCount = 10;

        //false:初始時沒有信號
        static AutoResetEvent autoResetEvent = new AutoResetEvent(false);

        static void Main(string[] args)
        {
            //開啓一個讀線程(子線程)
            Thread readerThread = new Thread(new ThreadStart(ReadThreadProc));
            readerThread.Name = "ReaderThread";
            readerThread.Start();

            for (int i = 1; i <= readWriteCount; i++)
            {
                Console.WriteLine("MainThread writing : {0}", i);

                //主(寫)線程將數據寫入
                myData = i;

                //主(寫)線程發信號,說明值已寫過了
                //即通知正在等待的線程有事件發生
                autoResetEvent.Set();

                Thread.Sleep(1);
            }

            //終止線程
            readerThread.Abort();
        }

        static void ReadThreadProc()
        {
            while (true)
            {
                //在數據被寫入前,讀線程等待(其實是等待寫線程發出數據寫完的信號)
                autoResetEvent.WaitOne();
                Console.WriteLine("{0} reading : {1}", Thread.CurrentThread.Name, myData);
            }
        }
    }
}

 

運行結果以下:

 

 

 

有結果可知,當主線程睡眠時間大於0值時,讀線程即有足夠的時間讀取寫線程寫入的數據。這個睡眠時間的長短能夠根據實際應用中子線程的計算量設定。

 

4. 對1再進行修改


主線程在寫完數據後根本不睡嗎呢?這個時候會發生什麼事情?

using System;
using System.Collections.Generic;
using System.Text;

using System.Threading;

namespace TestAutoResetEvent
{
    /// 
    /// 讀寫者線程
    /// 主線程寫,子線程讀,且只有將數據寫入後,讀線程才能將其讀出
    /// 
    class Program
    {
        //寫線程將數據寫入myData
        static int myData = 100;

        //讀寫次數
        const int readWriteCount = 10;

        //false:初始時沒有信號
        static AutoResetEvent autoResetEvent = new AutoResetEvent(false);

        static void Main(string[] args)
        {
            //開啓一個讀線程(子線程)
            Thread readerThread = new Thread(new ThreadStart(ReadThreadProc));
            readerThread.Name = "ReaderThread";
            readerThread.Start();

            for (int i = 1; i <= readWriteCount; i++)
            {
                Console.WriteLine("MainThread writing : {0}", i);

                //主(寫)線程將數據寫入
                myData = i;

                //主(寫)線程發信號,說明值已寫過了
                //即通知正在等待的線程有事件發生
                autoResetEvent.Set();

                //Thread.Sleep(1);
            }

            //終止線程
            readerThread.Abort();
        }

        static void ReadThreadProc()
        {
            while (true)
            {
                //在數據被寫入前,讀線程等待(其實是等待寫線程發出數據寫完的信號)
                autoResetEvent.WaitOne();
                Console.WriteLine("{0} reading : {1}", Thread.CurrentThread.Name, myData);
            }
        }
    }
}

 

運行結果以下:

 

 

 

有結果可知,不睡眠的狀況和睡眠時間爲0(即Thread.Sleep(0);)效果產很少,只是不睡眠丟失的數據更多了。

 

5. 對1再修改


將傳遞給AutoResetEvent的構造函數的參數設置爲true,觀察運行結果。

 

using System;
using System.Collections.Generic;
using System.Text;

using System.Threading;

namespace TestAutoResetEvent
{
    /// 
    /// 讀寫者線程
    /// 主線程寫,子線程讀,且只有將數據寫入後,讀線程才能將其讀出
    /// 
    class Program
    {
        //寫線程將數據寫入myData
        static int myData = 100;

        //讀寫次數
        const int readWriteCount = 10;

        //false:初始時沒有信號
        static AutoResetEvent autoResetEvent = new AutoResetEvent(true);

        static void Main(string[] args)
        {
            //開啓一個讀線程(子線程)
            Thread readerThread = new Thread(new ThreadStart(ReadThreadProc));
            readerThread.Name = "ReaderThread";
            readerThread.Start();

            for (int i = 1; i <= readWriteCount; i++)
            {
                Console.WriteLine("MainThread writing : {0}", i);

                //主(寫)線程將數據寫入
                myData = i;

                //主(寫)線程發信號,說明值已寫過了
                //即通知正在等待的線程有事件發生
                autoResetEvent.Set();

                Thread.Sleep(0);
            }

            //終止線程
            readerThread.Abort();
        }

        static void ReadThreadProc()
        {
            while (true)
            {
                //在數據被寫入前,讀線程等待(其實是等待寫線程發出數據寫完的信號)
                autoResetEvent.WaitOne();
                Console.WriteLine("{0} reading : {1}", Thread.CurrentThread.Name, myData);
            }
        }
    }
}

 

運行結果以下:

 

 

 

 

若將主線程的睡眠時間改成任意非0值,其運行結果均爲下圖所示的結果。

 

 

                         

6. 其餘修改

 

將主線程調用AutoResetEvent對象的Set方法刪除,分別對AutoResetEvent的構造函數的參數爲false和true觀察運行結果。

 

爲false,運行結果以下圖所示。

 

 

 

 

爲true,運行結果以下圖所示。

 

 

 

至此,我想咱們應該明白AutoResetEvent構造函數的參數的意義了。false:無信號,子線程的WaitOne方法不會被自動調用;true:有信號,子線程的WaitOne方法會被自動調用。

相關文章
相關標籤/搜索