在前面幾節(jié)中和大家分享了線程的一些基礎(chǔ)使用方法,本章結(jié)合之前的分享來編寫一些日常開發(fā)中應(yīng)用實(shí)例,和編寫多線程時(shí)一些注意點(diǎn)。如大家有好的實(shí)例也歡迎分享..
場景:系統(tǒng)中常常會有一些需要定時(shí)去循環(huán)執(zhí)行的存儲過程或方法等,這時(shí)就出現(xiàn)了定時(shí)任務(wù)小程序。
模型:查詢需定時(shí)執(zhí)行的計(jì)劃任務(wù)-->插入線程池-->執(zhí)行任務(wù)
static void MainMethod(){ Thread thead; thead = new Thread(QueryTask); thead.IsBackground = true; thead.Start(); Console.Read();}/// <summary>/// 查詢計(jì)劃任務(wù)/// </summary>static void QueryTask(){ TaskModel todo_taskModel; while (true) { int count = new Random().Next(1, 10); //模擬產(chǎn)生任務(wù)記錄數(shù) 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循環(huán)一次 }}/// <summary>/// 完成計(jì)劃任務(wù)/// </summary>/// <param name="taskModel"></param>static void InvokeThreadMethod(object taskModel){ TaskModel model = (TaskModel)taskModel; Console.WriteLine("執(zhí)行任務(wù)ID:{0}......執(zhí)行完成.{1}", model.TaskID, model.ITime);}/// <summary>/// 任務(wù)實(shí)體/// </summary>class TaskModel{ public int TaskID { get; set; } public DateTime ITime { get; set; }}
1.查詢計(jì)劃任務(wù)時(shí)只管有任務(wù)就推給線程池,不等待線程池中任務(wù)是否完成.(避免有些計(jì)劃任務(wù)耗時(shí)比較長,阻塞后面定時(shí)任務(wù)執(zhí)行時(shí)間)每隔30秒循環(huán)一次計(jì)劃任務(wù).
2.每條計(jì)劃任務(wù)完成后打印相應(yīng)信息.(也可記錄日志)
3.如果查詢計(jì)劃任務(wù)記錄數(shù)較多,可調(diào)整相應(yīng)的循環(huán)時(shí)間間隔。避免任務(wù)插入線程池時(shí)間過長阻塞后面定時(shí)任務(wù)執(zhí)行時(shí)間。
場景:在我們?nèi)粘O到y(tǒng)中會存在很多接口數(shù)據(jù)需要實(shí)時(shí)推送給第三方平臺(如:會員信息發(fā)生變化推送給微信平臺..)。這時(shí)我們就需寫一些數(shù)據(jù)推送程序。
模型:檢索任務(wù)-->插入線程池-->等待線程池任務(wù)完成-->提示任務(wù)完成
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); } //等待所有線程執(zhí)行完畢 countdown.WaitAll(); Console.WriteLine("線程池任務(wù)已完成.完成時(shí)間:{0}", DateTime.Now); Thread.Sleep(30000); }}static void InvokeThreadMethodB(object obj){ TaskModeB model = (TaskModeB)obj; Thread.Sleep(1000); Console.WriteLine("執(zhí)行任務(wù)ID:{0},執(zhí)行時(shí)間:{1},完成時(shí)間:{2}", model.TaskId, model.ITime, DateTime.Now); //發(fā)送信號量 本線程執(zhí)行完畢 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> /// 構(gòu)造函數(shù) /// </summary> /// <param name="total">需要等待執(zhí)行的線程總數(shù)</param> public MutipleThreadResetEvent(int total) { this.total = total; current = total; done = new ManualResetEvent(false); } /// <summary> /// 喚醒一個等待的線程 /// </summary> public void SetOne() { // Interlocked 原子操作類 ,此處將計(jì)數(shù)器減1 if (Interlocked.Decrement(ref current) == 0) { //當(dāng)所以等待線程執(zhí)行完畢時(shí),喚醒等待的線程 done.Set(); } } /// <summary> /// 等待所以線程執(zhí)行完畢 /// </summary> public void WaitAll() { done.WaitOne(); } /// <summary> /// 釋放對象占用的空間 /// </summary> public void Dispose() { ((IDisposable)done).Dispose(); }}
QueryTaskB()在檢索任務(wù)記錄數(shù)后會記錄任務(wù)條數(shù),并實(shí)例化對應(yīng)的ManualResetEvent數(shù)組,做為參數(shù)傳給線程池中線程任務(wù)。
最后等待線程池中所有任務(wù)執(zhí)行完成。后續(xù)可根據(jù)實(shí)際需要編寫各自業(yè)務(wù)邏輯。
兩個實(shí)例的不同點(diǎn):是否等待線程池中的所有任務(wù)完成。
實(shí)例1中定時(shí)任務(wù)對執(zhí)行的時(shí)間要求比較高,到了某個時(shí)間點(diǎn)必須執(zhí)行某個任務(wù)。所以不能等待線程池中的任務(wù)完成。
缺點(diǎn):有任務(wù)就插入線程池,有些任務(wù)可能會執(zhí)行很久,線程池每隔30秒循環(huán)一次,最后會導(dǎo)致線程池中有存在很多耗時(shí)很長的任務(wù)在線程池中未執(zhí)行完。當(dāng)線程池中線程數(shù)達(dá)到1023(線程池默認(rèn)最大線程數(shù))后線程池就不會在創(chuàng)建新的線程數(shù)去完成新的任務(wù),只能等待當(dāng)前線程池中線程數(shù)得到釋放。
實(shí)例2中數(shù)據(jù)推送程序?qū)ν扑偷臅r(shí)間要求相對1中要低一點(diǎn)。接口表中只要檢索到數(shù)據(jù)就推送給第三方平臺。一般每次傳輸數(shù)據(jù)量不是很多,但很頻繁。
缺點(diǎn):當(dāng)推送數(shù)據(jù)量大時(shí),執(zhí)行任務(wù)時(shí)間可能會較長。主線程會等待線程池中的所有任務(wù)完成。所有每次循環(huán)檢索任務(wù)的時(shí)間間隔可能會出現(xiàn)30S+NS現(xiàn)象。
新聞熱點(diǎn)
疑難解答
圖片精選