線程,進程,關系我不就不在BB了。
關於線程,其實我相信大家都了解了很多,此處我只是發表我對線程的理解和認識,不喜勿噴。如有不對之處還請大家指出。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Thread t = new Thread(Runing); 6 t.Name = "測試線程"; 7 t.Start(); 8 Console.ReadLine(); 9 } 10 11 static void Runing() 12 { 13 Console.WriteLine(Thread.CurrentThread.Name + " :" + DateTime.Now.ToString()); 14 } 15 }
上述代碼大家肯定都不陌生~!
接下來我們修改一下程序,完成單線程處理任務。我們知道很多時候,當我們的程序設計,又多個客戶端或者稱多個請求來源,並發請求來了以後,我們需要按照隊列處理事情比如秒殺下單
public class MyThread1 { //通知一個或多個正在等待的線程已發生事件 ManualResetEvent mre = new ManualResetEvent(false); //服務器的運行標識 bool isRuning = true; //線程安全的隊列 System.Collections.Concurrent.ConcurrentQueue<string> cqueue = new System.Collections.Concurrent.ConcurrentQueue<string>(); public MyThread1() { Thread t = new Thread(RunTest); t.Name = "我是測試線程"; t.Start(); } //模擬新增任務 public void add(int i) { //添加任務到隊列 cqueue.Enqueue("" + i); //喚醒所有相關的掛起線程 mre.Set(); } static void Main(string[] args) { MyThread1 p = new MyThread1(); for (int i = 0; i < 10; i++) { p.add(i); } Console.ReadLine(); } void RunTest() { //主循環 服務器運行標識 while (isRuning) { //如果是空則繼續等待 服務器運行標識 while (cqueue.IsEmpty && isRuning) { Console.WriteLine(DateTime.Now.ToString("HH:mm:ss:ffff") + " : " + Thread.CurrentThread.Name + " sleep"); //重置線程暫停狀態 mre.Reset(); //這個操作是以便服務器需要停止操作, //如果停止調用線程的Thread.Abort()是會導致處理隊列任務丟失 mre.WaitOne(2000); } string ret; //取出隊列任務 if (cqueue.TryDequeue(out ret)) { //測試輸出任務 Console.WriteLine(DateTime.Now.ToString("HH:mm:ss:ffff") + " : " + Thread.CurrentThread.Name + " " + ret); } } } }
通過add實現並發下單 RunTest() 方法來實現處理邏輯,
此處通過 ManualResetEvent 實現對線程的掛起和喚醒操作。當隊列為空的時候,線程自動進入掛起狀態,當有新的任務,add操作的時候直接喚醒掛起的線程。立即進入處理狀態。
為什麼選用 ManualResetEvent 這個線程通知這裡就不在解釋了有興趣的可以自己百度~!
為了避免在需要關閉服務器的時候調用線程的Thread.Abort() 導致後續隊列操作失效,所以加入了isRuning的bool變量實現線程是否繼續運行。
上述功能僅僅是永遠類似於處理下單,需要單線程隊列處理情況。各位看官請自行分析需求~~!
有了上述單線程處理隊列需求,我們也許會想到那麼在程序運行中自然有多線程處理隊列。
比如我們記錄日志的情況,(打個比方而已如果你很喜歡log4Net or log4J 請繞道) 我們需要提交日志記錄,但是不想這個操作耽誤程序的正常運行,且想日志這樣的記錄程序肯定不能單一線程處理,
如果日志瘋狂記錄,那麼勢必會導致處理不及時內存暴漲溢出問題
於是再次修改一下程序
1 public class MyThread3 2 { 3 //通知一個或多個正在等待的線程已發生事件 4 ManualResetEvent mre = new ManualResetEvent(false); 5 //服務器的運行標識 6 bool isRuning = true; 7 //線程安全的隊列 8 System.Collections.Concurrent.ConcurrentQueue<string> cqueue = new System.Collections.Concurrent.ConcurrentQueue<string>(); 9 //計數存儲器 10 Dictionary<string, int> cdic = new Dictionary<string, int>(); 11 12 public MyThread3() 13 { 14 List<Thread> ts = new List<Thread>(); 15 for (int i = 0; i < 4; i++) 16 { 17 Thread t = new Thread(RunTest); 18 t.Name = "我是線程(" + i + ")"; 19 cdic[t.Name] = 0; 20 t.Start(); 21 ts.Add(t); 22 } 23 } 24 25 //模擬新增任務 26 public void Add() 27 { 28 Thread t1 = new Thread(() => 29 { 30 for (int i = 0; i < 40; i++) 31 { 32 //添加任務到隊列 33 cqueue.Enqueue("日志記錄 " + i); 34 //喚醒所有相關的掛起線程 35 mre.Set(); 36 } 37 38 }); 39 t1.Start(); 40 41 } 42 43 //輸出計數器 44 public override string ToString() 45 { 46 foreach (var item in cdic) 47 { 48 Console.WriteLine(item.Key + " 計數 " + item.Value); 49 } 50 return ""; 51 } 52 53 54 static void Main(string[] args) 55 { 56 MyThread3 p = new MyThread3(); 57 p.Add(); 58 Console.ReadLine(); 59 p.ToString(); 60 Console.WriteLine(); 61 Console.ReadLine(); 62 } 63 64 void RunTest() 65 { 66 //主循環 服務器運行標識 67 while (isRuning) 68 { 69 //如果是空則繼續等待 服務器運行標識 70 while (cqueue.IsEmpty && isRuning) 71 { 72 //重置線程暫停狀態 73 mre.Reset(); 74 //這個操作是以便服務器需要停止操作, 75 //如果停止調用線程的Thread.Abort()是會導致處理隊列任務丟失 76 mre.WaitOne(2000); 77 Console.WriteLine(DateTime.Now.ToString("HH:mm:ss:ffff") + " : " + Thread.CurrentThread.Name + " sleep"); 78 } 79 string ret; 80 //取出隊列任務 81 if (cqueue.TryDequeue(out ret)) 82 { 83 //測試輸出任務 84 Console.WriteLine(DateTime.Now.ToString("HH:mm:ss:ffff") + " : " + Thread.CurrentThread.Name + " " + ret); 85 //添加任務的計數器,為了查看最後線程執行任務的計數 86 cdic[Thread.CurrentThread.Name] = cdic[Thread.CurrentThread.Name] + 1; 87 } 88 } 89 } 90 }
輸出
多個線程實現了對日志記錄處理,並且在空閒時間實現線程暫停,有任務喚醒,以保證,不浪費資源同時能即時處理~!
由於第一次寫博客,語言組織能力差,大家就看看程序代碼和注釋吧!
大家多多指教。