電商的秒殺和搶購,對程序員來講,都不是一個陌生的東西。然而,從技術的角度來講,這對於Web系統是一個巨大的考驗。當一個Web系統,在一秒鐘內收到數以萬計甚至更多請求時,系統的優化和穩定相當重要。程序員
咱們直接將請求放入隊列Queue中的,採用FIFO(First Input First Output,先進先出),這樣的話,咱們就不會致使某些請求永遠獲取不到鎖。這裏有點強行將多線程變成單線程的感受。多線程
秒殺看似簡單,可是可能會存在兩個問題:高併發和超賣架構
高併發:比較火秒殺活動同時參與秒殺人數都是10w+的,如此之高的秒殺人數對於網站架構從前到後都是一種考驗。併發
超賣:秒殺商品都會有固定的數量,如何避免成功下訂單買到商品的人數不超過商品數量的上限,這是每一個搶購活動都要面臨的一大難題。ide
此代碼簡單說明問題,例若有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萬用戶併發請求ui
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 + "件"); });
處理中,因爲數據太多看不到誰搶到了商品,後面註釋了搶不到的輸出spa
用戶0、一、75000、50000、5000一、50002 、50003 、5000四、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()); } } }
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 + "件"); } }); } }