本篇體驗使用C#的泛型隊列Queue<T>實現生產消費模式。多線程
若是把生產消費想像成自動流水生產線的話,生產就是流水線的物料,消費就是某種設備對物料進行加工的行爲,流水線就是隊列。dom
如今,要寫一個體現生產消費模式的泛型幫助類,好比叫ProducerConsumer<T>。函數
該類確定會維護一個有關生產、物料的Queue<T>類型的字段,還存在一個有關消費、Action<T>類型的字段。this
在ProducerConsumer類的構造函數中,爲Action<T>類型的字段賦值,並開啓後臺有關消費的線程。spa
ProducerConsumer類確定存在一個進隊列的方法,而且要保證在多線程狀況下,同一時間只有一個生產或物料進入隊列。線程
ProducerConsumer類還存在一個有關消費的方法,而且保證在多線程狀況下,同一時間只有一個生產或物料出列,並消費它。隊列
另外,在生產或物料在出隊列的時候,可能會出現隊列中暫時沒有生產或物料的狀況,這時候咱們但願線程阻塞一下,這須要經過AutoResetEvent實現。AutoResetEvent的大體原理是:當生產或物料進入隊列的時候須要告訴AutoResetEvent一下,當隊列中暫時沒有生產或物料的時候,也須要告訴AutoResetEvent,讓它來阻塞線程。事件
//有關生產消費的泛型類public class ProducerConsumer<T>{//用來存儲生產者的隊列private readonly Queue<T> queue = new Queue<T>();//鎖private readonly object queueLocker = new object();//消費行爲private readonly Action<T> consumerAction;//出列的時候須要檢查隊列中是否有元素,若是沒有,須要阻塞private readonly AutoResetEvent queueWaitHandle = new AutoResetEvent(false);public ProducerConsumer(Action<T> consumerAction){if (consumerAction == null){throw new ArgumentNullException("consumerAction");}this.consumerAction = consumerAction;//後臺開啓一個線程開始消費生產者new Thread(this.ConsumeItems){IsBackground = true}.Start();}//進列public void Enqueue(T item){//確保同一時間只有一個生產者進列lock (queueLocker){queue.Enqueue(item);//每次進列都要設置AutoResetEvent事件this.queueWaitHandle.Set();}}//消費動做private void ConsumeItems(){while (true){T nextItem = default(T);//標誌,確認隊列中的生產者是否存在bool doesItemExist;//確保同一時間只有一個生產者出列lock (this.queueLocker){//先確認隊列中的生產者是否存在doesItemExist = this.queue.Count > 0;if (doesItemExist){nextItem = this.queue.Dequeue();}}//若是生產者存在,才消費生產者if (doesItemExist){this.consumerAction(nextItem);}else//不然的話,再等等下一個隊列中的生產者{this.queueWaitHandle.WaitOne();}}}}
客戶端,針對多線程情形。string
class Program{static void Main(string[] args){//實例化一個int類型的生產消費實例var producerConsumer = new ProducerConsumer<int>(i => Console.WriteLine("正在消費" + i));Random random = new Random();//開啓進隊列線程var t1 = new Thread(() =>{for (int i = 0; i < 100; i++)
{producerConsumer.Enqueue(i);Thread.Sleep(random.Next(0,5));}});var t2 = new Thread(() =>
{for (int i = 0; i > -100; i--){producerConsumer.Enqueue(i);Thread.Sleep(random.Next(0, 5));}});t1.Start();t2.Start();t1.Join();t2.Join();Thread.Sleep(50);Console.ReadKey();}}