C#队列Queue实现一个简单的电商网站秒杀程序

时间:2024-03-06 13:47:49

电商的秒杀和抢购,对程序员来说,都不是一个陌生的东西。然而,从技术的角度来说,这对于Web系统是一个巨大的考验。当一个Web系统,在一秒钟内收到数以万计甚至更多请求时,系统的优化和稳定至关重要。

我们直接将请求放入队列Queue中的,采用FIFO(First Input First Output,先进先出),这样的话,我们就不会导致某些请求永远获取不到锁。这里有点强行将多线程变成单线程的感觉。

秒杀看似简单,但是可能会存在两个问题:高并发和超卖

高并发:比较火秒杀活动同时参与秒杀人数都是10w+的,如此之高的秒杀人数对于网站架构从前到后都是一种考验。

超卖:秒杀商品都会有固定的数量,如何避免成功下订单买到商品的人数不超过商品数量的上限,这是每个抢购活动都要面临的一大难题。

此代码简单说明问题,例如有10万个人秒杀10个商品,我们定义队列queueAll 存放并发的10万人,queueCur存放已经抢到的10个人

        private static int cnt =10;
        private static Queue<string> queueAll = new Queue<string>();
        private static Queue<string> queueCur = new Queue<string>();

购买代码

        public RetData Buy(string uid)
        {
            queueAll.Enqueue(uid);

            if (queueAll.Count > cnt){
                return new RetData {Code = -1, Msg = "商品抢光了", Cnt = 0};
            }
            queueCur.Enqueue(uid);
            return new RetData { Code = 1, Msg = "恭喜已抢到", Cnt = cnt - queueAll.Count };
        }
        public class RetData
        {
            public int Code { get; set; }
            public string Msg { get; set; }
            public int Cnt { get; set; }
        }

处理订单

        public static void HandleQueue()
        {
            Task.Factory.StartNew(() =>
            {
                while (true) if (queueCur.Count > 0) HandleOrder();
            });
        }

        public static void HandleOrder()
        {
            while (queueCur.Count != 0)
            {
                Console.WriteLine("处理用户订单中:" + queueCur.Dequeue());
            }
        }

Parallel模拟10万用户并发请求

            var tt = new ThreadPar();
            Parallel.For(0, 100000, (t, state) =>
            {
                var uid = "用户" + t;
                var x = tt.Buy(uid);
                if (x.Code == -1)
                {
                    Console.WriteLine(uid + ":" + x.Msg);
                    //state.Break();
                }
                else
                    Console.WriteLine(uid + ":" + x.Msg + "还剩下:" + x.Cnt + "件");
            });

处理中,由于数据太多看不到谁抢到了商品,后面注释了抢不到的输出

用户0、1、75000、50000、50001、50002 、50003 、50004、25000、75001 这10位抢到了商品,其他人都没有抢到,

全部代码示例:

    public class ThreadPar
    {
        private static int cnt =10;
        private static Queue<string> queueAll = new Queue<string>();
        private static Queue<string> queueCur = new Queue<string>();
        //private static  object ol = new object();

        static ThreadPar()
        {
            HandleQueue();
        }

        public RetData Buy(string uid)
        {
            queueAll.Enqueue(uid);

            if (queueAll.Count > cnt){
                return new RetData {Code = -1, Msg = "商品抢光了", Cnt = 0};
            }
            queueCur.Enqueue(uid);
            return new RetData { Code = 1, Msg = "恭喜已抢到", Cnt = cnt - queueAll.Count };
        }
        public class RetData
        {
            public int Code { get; set; }
            public string Msg { get; set; }
            public int Cnt { get; set; }
        }
        public static void HandleQueue()
        {
            Task.Factory.StartNew(() =>
            {
                while (true) if (queueCur.Count > 0) HandleOrder();
            });
        }

        public static void HandleOrder()
        {
            while (queueCur.Count != 0)
            {
                Console.WriteLine("处理用户订单中:" + queueCur.Dequeue());
            }
        }

    }
View Code
    class Program
    {
        static void Main()
        {
            ThreadBuy();

            Console.WriteLine("----------操作完成----------");
            Console.ReadKey();
        }

        //秒杀
        static void ThreadBuy()
        {
            System.Threading.Thread.Sleep(10000);
            var tt = new ThreadPar();
            Parallel.For(0, 1000000, (t, state) =>
            {
                var uid = "用户" + t;
                var x = tt.Buy(uid);
                if (x.Code == -1)
                {
                    Console.WriteLine(uid + ":" + x.Msg);
                    //state.Break();
                }
                else
                {
                    Console.WriteLine(uid + ":" + x.Msg + "还剩下:" + x.Cnt + "");
                }
                    
            });

        }

    }
控制台程序