C# 隊列和棧 線程安全


隊列是其元素以先進先出(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; }
    }
}
View Code

執行結果如下:

由此可見,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 ...)

今天只是做個整理,沒什么特別的意義,有興趣的小虎斑點個贊吧!謝謝!

@陳卧龍的博客


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM