C# ManualResetEvent的理解和用法
ManualResetEvent是C#中一個比較常用的工具,可用于線程間通信,實現(xiàn)一種類似信號量的功能(不知道我這樣描述是否恰當,有可能不是“類似”,而“就是”通過信號量來實現(xiàn)的,因為我也是最近才知道這個類,以前一直不知道,哈哈。如果有哪位清楚的話,請給我解惑。)。
先了解一下ManualResetEvent的基本用法:
1、初始化:public ManualResetEvent(bool initialState);
ManualResetEvent的構造方法有個bool型參數(shù),當為true時,則表示有信號,為false時,則表示無信號。這個怎么理解呢?我們接著看ManualResetEvent3個基本方法中的WaitOne方法。
2、WaitOne方法:WaitOne方法有幾種4種重載,我在這里只對它的功能進行分析。
WaitOne方法,顧名思義,它會具有一種等待的功能,也就是線程阻塞。這里的阻塞功能是有條件的,當無信號時,它是阻塞的,有信號時,它將無任何阻塞,被執(zhí)行時就直接跳過了(這個從邏輯上應該挺好理解:當有信號需要處理時,需要立即處理,沒有任何信號時,就當然要等一等了)。所以,回顧到1,當初始化ManualResetEvent時,initialState為false,WaitOne將會有阻塞效果,否則,沒有阻塞效果。
3、Set方法:將ManualResetEvent對象的信號狀態(tài)設為有信號狀態(tài),這個時候WaitOne如果正在阻塞中的話,將會立即終止阻塞,向下繼續(xù)執(zhí)行。而且這個狀態(tài)一直不變的話,每次執(zhí)行到WaitOne都將無任何阻塞。
4、Reset方法:將ManualResetEvent對象的信號狀態(tài)設為無信號狀態(tài),當下次執(zhí)行到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初始化狀態(tài)為無信號,將使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/次)。而另外一個消費線程也是與生產線同時運行的,我想實現(xiàn)一個目標:每當有產品可以消費時,我將立即消費,不想有任何延遲。
按照以前最簡單常用的思路,就是讓消費線程每次運行sleep一下,但是這個需要循環(huán)時間足夠短、循環(huán)頻率足夠快才行,頻率至少要高于任意一個生產線程,即sleep時間小于生產線程中sleep時間的最小值。
如果這樣實現(xiàn),代碼從邏輯來講是沒有任何問題的,但是效率太低了,而且可能遭遇麻煩。假設這樣一種情況,如果生產線程的生產頻率是不固定的(不像我們這固定sleep幾秒鐘,這個在真實情況中是存在的),有時候1小時才生產一次,有時候100毫秒生產一次(笑,這個比較極端啊),那么我們至少需要將消費線程的sleep時間低于100毫秒才行。這樣的話當生產線程1個小時一次的時候是不是也太浪費了,基本上消費線程在空轉。
所以嘛,才有了我這樣一個代碼,我的消費線程每次循環(huán)都會檢查已經(jīng)生產出來的產品數(shù)量,當有產品可供消費的時候,我就一次消費光,并且提醒:“已經(jīng)沒有可消費的產品了,下次可能需要等等了!”(調用Reset方法),那么下次循環(huán)時,檢查到果然沒有產品了,那么就將等待了(WaitOne方法阻塞)。這時候消費線程就會完全停在這了,不會每次都空轉,是不是比較人性化?呵呵。
接下來,任意一個生產線程如果生產出新的產品,就將會通知消費線程:“嘿,伙計,你要的東西來了,快醒醒吧!”(調用Set方法),這樣消費線程就會立馬繼續(xù)運行(WaitOne方法將會繼續(xù)向下執(zhí)行,并且在再次Reset前,它都不會再阻塞了)。當然,消費線程得下次循環(huán)將檢測到有產品可供消費了,它又會將產品消費完,并且又提醒:“已經(jīng)沒有可消費的產品了,下次可能需要等等了!”(調用Reset方法)。就這樣生命不息,循環(huán)往復。
轉載于:https://www.cnblogs.com/modify/archive/2013/01/10/2855014.html
總結
以上是生活随笔為你收集整理的C# ManualResetEvent的理解和用法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 八杯水微博程序演示及下载
- 下一篇: 人民的胜利 爱奇艺黄金VIP恢复720P