隊列是其元素以先進先出(FIFO)的方式來處理集合,先入隊的元素會先讀取。
棧是和隊列非常類似的另一個容器,棧和隊列最大的區別是后進先出(LIFO),也可以說成先進后出。
隊列在現實生活中的例子數不勝數。例如:排隊打飯,排隊購買機票,打印隊列中等待處理的打印業務等
棧在生活中的例子也不少。例如:物流裝車,火車調度等
那么,關於隊列和棧在C#的用法介紹如下:
隊列主要用法:
棧主要用法:
上述兩個圖中分別展示了隊列:Queue<T>及棧Stack<T>
而在實際的C#編程中,無論是Queue<T>還是Stack<T>都無法保證線程安全,換句話說,他們存在普通集合List<T>存在的並發問題。關於這個問題,大家可以參考我的博客:C# 集合-並發處理-鎖OR線程
今天,我寫簡單寫了一個案例,分享給大家,代碼如下:

using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace StackQueue { class Program { public static Queue<Product> productQueue = new Queue<Product>(50000);//定義一個隊列-----存在並發風險 public static Stack<Product> productStack = new Stack<Product>(50000);//定義一個棧-----存在並發風險 //Net 4.0以后,微軟提供了線程安全的先進先出集合 public static ConcurrentQueue<Product> productCQ = new ConcurrentQueue<Product>();//無需考慮並發 //Net 4.0以后,微軟提供了線程安全的先進后出集合 public static ConcurrentStack<Product> productSK = new ConcurrentStack<Product>();//無需考慮並發 static void Main(string[] args) { //普通入隊操作 存在並發風險 Task t1 = new TaskFactory().StartNew(RuDui); Task t2 = new Task(()=>RuDui()); t2.Start(); Task t3 = Task.Factory.StartNew(RuDui); Task t4 = Task.Run(() => RuDui()); Task.WaitAll(t1, t2, t3, t4); // // //普通入棧操作,存在並發風險 Task t5 = new TaskFactory().StartNew(RuZhan); Task t6 = new Task(() => RuZhan()); t6.Start(); Task t7 = Task.Factory.StartNew(RuZhan); Task t8 = Task.Run(() => RuZhan()); Task.WaitAll(t5, t6, t7, t8); // // //線程安全的入隊操作,無需考慮並發問題<微軟底層幫你處理了 ~_~ > Task t11 = new TaskFactory().StartNew(RuDuiCC); Task t22 = new Task(() => RuDuiCC()); t22.Start(); Task t33 = Task.Factory.StartNew(RuDuiCC); Task t44 = Task.Run(() => RuDuiCC()); Task.WaitAll(t11, t22, t33, t44); // // //線程安全的入棧操作,無需考慮並發問題<微軟底層幫你處理了 ~_~ > Task t55 = new TaskFactory().StartNew(RuZhanCC); Task t66 = new Task(() => RuZhanCC()); t66.Start(); Task t77 = Task.Factory.StartNew(RuZhanCC); Task t88 = Task.Run(() => RuZhanCC()); Task.WaitAll(t55, t66, t77, t88); // // Console.WriteLine("productQueue隊列中共有元素:" + productQueue.Count + "個。實際應該有40000個元素。存在並發風險!"); Console.WriteLine("productStack棧中共有元素:" + productStack.Count + "個。實際應該有40000個元素。存在並發風險!"); Console.WriteLine("productCQ隊列中共有元素:" + productCQ.Count + "個。實際應該有40000個元素。無需考慮並發風險!"); Console.WriteLine("productSK棧中共有元素:" + productSK.Count + "個。實際應該有40000個元素。無需考慮並發風險!"); Console.ReadKey(); } public static void RuDui() //定義一個入隊方法 先進先出 { for (int i = 1; i < 10001; i++) { Product model = new Product() { Name = "商品" + i, Category = "水果", SellPrice = 10 }; productQueue.Enqueue(model); } } public static void RuZhan() //定義一個入棧方法 先進后出 { for (int i = 1; i < 10001; i++) { Product model = new Product() { Name = "商品" + i, Category = "水果", SellPrice = 10 }; productStack.Push(model); } } public static void RuDuiCC() //保證線程安全的入隊方法 { for (int i = 1; i < 10001; i++) { Product model = new Product() { Name = "商品" + i, Category = "水果", SellPrice = 10 }; productCQ.Enqueue(model); } } public static void RuZhanCC() //保證線程安全的入棧方法 { for (int i = 1; i < 10001; i++) { Product model = new Product() { Name = "商品" + i, Category = "水果", SellPrice = 10 }; productSK.Push(model); } } } public class Product { public string Name { get; set; } public string Category { get; set; } public int SellPrice { get; set; } } }
執行結果如下:
由此可見,Queue<T>入隊和Stack<T>入棧都存在並發的問題。
上述代碼中,提供了ConcurrentQueue<T> 和 ConcurrentStack<T> 兩種全新的隊列、棧。代碼注釋中也提到,這是NETFRM4.0才提出的線程安全隊列及棧集合。
換句話說,使用ConcurrentQueue<T> 和 ConcurrentStack<T> 兩種全新的隊列、棧,無需考慮並發問題!
當然,如果您不習慣使用ConcurrentQueue<T> 和 ConcurrentStack<T> ,那么您也可以結合C# Lock(Object) 進行代碼臨界,這種加鎖的方式也能解決Queue<T>和Stack<T>並發的問題。不過,加鎖就意味着額外開銷,效率會低一些。
加鎖的方式在我的博客:C# 集合-並發處理-鎖OR線程 中都有體現。
更多興趣閱讀<也是關於隊列應用的哦>:C# 定時器和隊列結合,賣包子啦,Timer、 AutoResetEvent、 ManualResetEvent
當然,如果遍歷隊列或者棧,可以使用Foreach(var A in ...)
今天只是做個整理,沒什么特別的意義,有興趣的小虎斑點個贊吧!謝謝!
@陳卧龍的博客