C# ManualResetEvent的理解和用法

來源:互聯網
上載者:User

ManualResetEvent是C#中一個比較常用的工具,可用於線程間通訊,實現一種類似訊號量的功能(不知道我這樣描述是否恰當,有可能不是“類似”,而“就是”通過訊號量來實現的,因為我也是最近才知道這個類,以前一直不知道,哈哈。如果有哪位清楚的話,請給我解惑。)。

先瞭解一下ManualResetEvent的基本用法:

1、初始化:public ManualResetEvent(bool initialState);

  ManualResetEvent的構造方法有個bool型參數,當為true時,則表示有訊號,為false時,則表示無訊號。這個怎麼理解呢?我們接著看ManualResetEvent3個基本方法中的WaitOne方法。

2、WaitOne方法:WaitOne方法有幾種4種重載,我在這裡只對它的功能進行分析。

  WaitOne方法,顧名思義,它會具有一種等待的功能,也就是線程阻塞。這裡的阻塞功能是有條件的,當無訊號時,它是阻塞的,有訊號時,它將無任何阻塞,被執行時就直接跳過了(這個從邏輯上應該挺好理解:當有訊號需要處理時,需要立即處理,沒有任何訊號時,就當然要等一等了)。所以,回顧到1,當初始化ManualResetEvent時,initialState為false,WaitOne將會有阻塞效果,否則,沒有阻塞效果。

3、Set方法:將ManualResetEvent對象的訊號狀態設為有訊號狀態,這個時候WaitOne如果正在阻塞中的話,將會立即終止阻塞,向下繼續執行。而且這個狀態一直不變的話,每次執行到WaitOne都將無任何阻塞。

4、Reset方法:將ManualResetEvent對象的訊號狀態設為無訊號狀態,當下次執行到WaitOne時,又將重新開始阻塞。

呵呵,按我個人理解,ManualResetEvent得幾個方法的功能大致就這個意思。嗯,口說無憑,代碼才是王道。接下來我用一個生產消費模型的例子來給大家班門弄斧一下!

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;namespace ThreadTest{    class Program    {        static void Main(string[] args)        {            new ProductAndCostTester();        }    }    /// <summary>    /// 生產消費模型    /// </summary>    public class ProductAndCostTester    {        /// <summary>        /// 生產線1線程        /// </summary>        private Thread _producterThread1;        /// <summary>        /// 生產線2線程        /// </summary>        private Thread _producterThread2;        /// <summary>        /// 消費線線程        /// </summary>        private Thread _costerThread;        /// <summary>        /// 產品列表        /// </summary>        private List<int> _goodList;        /// <summary>        /// ManualResetEvent執行個體        /// </summary>        private ManualResetEvent _mre;        public ProductAndCostTester()        {            _goodList = new List<int>();            _mre = new ManualResetEvent(false);//false初始化狀態為無訊號,將使WaitOne阻塞            _producterThread1 = new Thread(Product1);            _producterThread1.Name = "Productor1";            _producterThread1.Start();            _producterThread2 = new Thread(Product2);            _producterThread2.Name = "Productor2";            _producterThread2.Start();            _costerThread = new Thread(Cost);            _costerThread.Name = "Costor";            _costerThread.Start();        }        /// <summary>        /// 生產線1        /// </summary>        void Product1()        {            while (true)            {                Console.WriteLine(Thread.CurrentThread.Name + ":" + DateTime.Now.ToString("HH:mm:ss"));                for (int i = 0; i < 3; i++)                {                    _goodList.Add(1);                }                _mre.Set();//表示有訊號了,通知WaitOne不再阻塞                Thread.Sleep(8000);            }        }        /// <summary>        /// 生產線2        /// </summary>        void Product2()        {            while (true)            {                Console.WriteLine(Thread.CurrentThread.Name + ":" + DateTime.Now.ToString("HH:mm:ss"));                for (int i = 0; i < 6; i++)                {                    _goodList.Add(1);                }                _mre.Set();//表示有訊號了,通知WaitOne不再阻塞                Thread.Sleep(10000);            }        }        /// <summary>        /// 消費線        /// </summary>        void Cost()        {            while (true)            {                if (_goodList.Count > 0)                {                    Console.WriteLine("Cost " + _goodList.Count + " at " + DateTime.Now.ToString("HH:mm:ss"));                    _goodList.Clear();                    _mre.Reset();//重設為無訊號了,使WaitOne可以再次阻塞                }                else                {                    Console.WriteLine("No cost at " + DateTime.Now.ToString("HH:mm:ss"));                    _mre.WaitOne();//如果沒有可消費的產品,即無訊號,則會阻塞                }            }        }    }}

這個代碼是可以直接啟動並執行,我就不再用附件了。嗯,下面我來簡單講解一下我這個代碼想表達什麼:

這裡有3個線程,2條生產線和1條消費線。2條生產線同時進行,但是可能生產的速度不一致(這裡一個8s/次,一個10s/次)。而另外一個消費線程也是與生產線同時啟動並執行,我想實現一個目標:每當有產品可以消費時,我將立即消費,不想有任何延遲。

按照以前最簡單常用的思路,就是讓消費線程每次運行sleep一下,但是這個需要迴圈時間足夠短、迴圈頻率足夠快才行,頻率至少要高於任意一個生產線程,即sleep時間小於生產線程中sleep時間的最小值。

如果這樣實現,代碼從邏輯來講是沒有任何問題的,但是效率太低了,而且可能遭遇麻煩。假設這樣一種情況,如果生產線程的生產頻率是不固定的(不像我們這固定sleep幾秒鐘,這個在真實情況中是存在的),有時候1小時才生產一次,有時候100毫秒生產一次(笑,這個比較極端啊),那麼我們至少需要將消費線程的sleep時間低於100毫秒才行。這樣的話當生產線程1個小時一次的時候是不是也太浪費了,基本上消費線程在空轉。

所以嘛,才有了我這樣一個代碼,我的消費線程每次迴圈都會檢查已經生產出來的產品數量,當有產品可供消費的時候,我就一次消費光,並且提醒:“已經沒有可消費的產品了,下次可能需要等等了!”(調用Reset方法),那麼下次迴圈時,檢查到果然沒有產品了,那麼就將等待了(WaitOne方法阻塞)。這時候消費線程就會完全停在這了,不會每次都空轉,是不是比較人性化?呵呵。

接下來,任意一個生產線程如果生產出新的產品,就將會通知消費線程:“嘿,夥計,你要的東西來了,快醒醒吧!”(調用Set方法),這樣消費線程就會立馬繼續運行(WaitOne方法將會繼續向下執行,並且在再次Reset前,它都不會再阻塞了)。當然,消費線程得下次迴圈將檢測到有產品可供消費了,它又會將產品消費完,並且又提醒:“已經沒有可消費的產品了,下次可能需要等等了!”(調用Reset方法)。就這樣生命不息,迴圈往複。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.