在前面幾節中和你們分享了線程的一些基礎使用方法,本章結合以前的分享來編寫一些平常開發中應用實例,和編寫多線程時一些注意點。如你們有好的實例也歡迎分享..小程序
場景:系統中經常會有一些須要定時去循環執行的存儲過程或方法等,這時就出現了定時任務小程序。數組
模型:查詢需定時執行的計劃任務-->插入線程池-->執行任務微信
static void MainMethod() { Thread thead; thead = new Thread(QueryTask); thead.IsBackground = true; thead.Start(); Console.Read(); } /// <summary> /// 查詢計劃任務 /// </summary> static void QueryTask() { TaskModel todo_taskModel; while (true) { int count = new Random().Next(1, 10); //模擬產生任務記錄數 for (int i = 0; i < count; i++) { todo_taskModel = new TaskModel() { TaskID = i, ITime = DateTime.Now }; ThreadPool.QueueUserWorkItem(InvokeThreadMethod, todo_taskModel); } Thread.Sleep(30000); //30s循環一次 } } /// <summary> /// 完成計劃任務 /// </summary> /// <param name="taskModel"></param> static void InvokeThreadMethod(object taskModel) { TaskModel model = (TaskModel)taskModel; Console.WriteLine("執行任務ID:{0}......執行完成.{1}", model.TaskID, model.ITime); } /// <summary> /// 任務實體 /// </summary> class TaskModel { public int TaskID { get; set; } public DateTime ITime { get; set; } }
1.查詢計劃任務時只管有任務就推給線程池,不等待線程池中任務是否完成.(避免有些計劃任務耗時比較長,阻塞後面定時任務執行時間)每隔30秒循環一次計劃任務.多線程
2.每條計劃任務完成後打印相應信息.(也可記錄日誌)dom
3.若是查詢計劃任務記錄數較多,可調整相應的循環時間間隔。避免任務插入線程池時間過長阻塞後面定時任務執行時間。函數
場景:在咱們平常系統中會存在不少接口數據須要實時推送給第三方平臺(如:會員信息發生變化推送給微信平臺..)。這時咱們就需寫一些數據推送程序。this
模型:檢索任務-->插入線程池-->等待線程池任務完成-->提示任務完成spa
static void MainMethodB() { Thread thread = new Thread(QueryTaskB); thread.IsBackground = true; thread.Start(); Console.Read(); } static void QueryTaskB() { MutipleThreadResetEvent countdown; TaskModeB model;
while (true) { int RandomNumber = new Random().Next(100, 200); countdown = new MutipleThreadResetEvent(RandomNumber); for (int i = 0; i < RandomNumber; i++) { model = new TaskModeB() { TaskId = i, ITime = DateTime.Now, manualResetEvent = countdown }; ThreadPool.QueueUserWorkItem(InvokeThreadMethodB, model); } //等待全部線程執行完畢 countdown.WaitAll(); Console.WriteLine("線程池任務已完成.完成時間:{0}", DateTime.Now); Thread.Sleep(30000); } } static void InvokeThreadMethodB(object obj) { TaskModeB model = (TaskModeB)obj; Thread.Sleep(1000); Console.WriteLine("執行任務ID:{0},執行時間:{1},完成時間:{2}", model.TaskId, model.ITime, DateTime.Now); //發送信號量 本線程執行完畢 model.manualResetEvent.SetOne(); } class TaskModeB { public int TaskId { set; get; } public DateTime ITime { set; get; } public MutipleThreadResetEvent manualResetEvent { set; get; } } /// <summary> /// 解決問題:WaitHandle.WaitAll(evetlist)方法最大隻能等待64個ManualResetEvent事件 /// </summary> public class MutipleThreadResetEvent { private readonly ManualResetEvent done; private readonly int total; private long current; /// <summary> /// 構造函數 /// </summary> /// <param name="total">須要等待執行的線程總數</param> public MutipleThreadResetEvent(int total) { this.total = total; current = total; done = new ManualResetEvent(false); } /// <summary> /// 喚醒一個等待的線程 /// </summary> public void SetOne() { // Interlocked 原子操做類 ,此處將計數器減1 if (Interlocked.Decrement(ref current) == 0) { //當因此等待線程執行完畢時,喚醒等待的線程 done.Set(); } } /// <summary> /// 等待因此線程執行完畢 /// </summary> public void WaitAll() { done.WaitOne(); } /// <summary> /// 釋放對象佔用的空間 /// </summary> public void Dispose() { ((IDisposable)done).Dispose(); } }
QueryTaskB()在檢索任務記錄數後會記錄任務條數,並實例化對應的ManualResetEvent數組,作爲參數傳給線程池中線程任務。線程
最後等待線程池中全部任務執行完成。後續可根據實際須要編寫各自業務邏輯。日誌
兩個實例的不一樣點:是否等待線程池中的全部任務完成。
實例1中定時任務對執行的時間要求比較高,到了某個時間點必須執行某個任務。因此不能等待線程池中的任務完成。
缺點:有任務就插入線程池,有些任務可能會執行好久,線程池每隔30秒循環一次,最後會致使線程池中有存在不少耗時很長的任務在線程池中未執行完。當線程池中線程數達到1023(線程池默認最大線程數)後線程池就不會在建立新的線程數去完成新的任務,只能等待當前線程池中線程數獲得釋放。
實例2中數據推送程序對推送的時間要求相對1中要低一點。接口表中只要檢索到數據就推送給第三方平臺。通常每次傳輸數據量不是不少,但很頻繁。
缺點:當推送數據量大時,執行任務時間可能會較長。主線程會等待線程池中的全部任務完成。全部每次循環檢索任務的時間間隔可能會出現30S+NS現象。