• 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 ...)

    今天只是做个整理,没什么特别的意义,有兴趣的小虎斑点个赞吧!谢谢!

    @陈卧龙的博客

  • 相关阅读:
    利用jQuery Ajax技术实现每隔5秒向某页面传值
    POJ 题目2761 Feed the dogs(主席树||划分树)
    【翻译自mos文章】将expdp的dmp文件从asm磁盘组里边放到本地文件系统里边
    【算法拾遗】最大数和最小数
    WinMain和MFC的差别
    3.1 The Interpolating Polynomial(站点)
    在VS中设置比较和谐的字体和颜色的方法
    获取微信服务器IP地址
    关于bcg库记忆界面的问题及其解决办法
    Objective-C基础笔记(8)Foundation常用类NSString
  • 原文地址:https://www.cnblogs.com/chenwolong/p/Queue.html
Copyright © 2020-2023  润新知