在前面幾節中和大家分享了線程的一些基礎使用方法,本章結合之前的分享來編寫一些日常開發中應用實例,和編寫多線程時一些注意點。如大家有好的實例也歡迎分享..
場景:系統中常常會有一些需要定時去循環執行的存儲過程或方法等,這時就出現了定時任務小程序。
模型:查詢需定時執行的計劃任務-->插入線程池-->執行任務
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.每條計劃任務完成後打印相應信息.(也可記錄日志)
3.如果查詢計劃任務記錄數較多,可調整相應的循環時間間隔。避免任務插入線程池時間過長阻塞後面定時任務執行時間。
場景:在我們日常系統中會存在很多接口數據需要實時推送給第三方平台(如:會員信息發生變化推送給微信平台..)。這時我們就需寫一些數據推送程序。
模型:檢索任務-->插入線程池-->等待線程池任務完成-->提示任務完成
static void MainMethodB()
{
Thread thread = new Thread(QueryTaskB);
thread.IsBackground = true;
thread.Start();
}
static void QueryTaskB()
{
ManualResetEvent[] manualReset;
TaskModeB model;
while (true)
{
int RandomNumber = new Random().Next(0, 6);
manualReset = new ManualResetEvent[RandomNumber];
for (int i = 0; i < RandomNumber; i++)
{
manualReset[i] = new ManualResetEvent(false);
model = new TaskModeB() { TaskId = i, ITime = DateTime.Now, manualResetEvent = manualReset[i] };
ThreadPool.QueueUserWorkItem(InvokeThreadMethodB, model);
}
//等待線程池任務完成
ManualResetEvent.WaitAll(manualReset);
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.Set();
}
class TaskModeB
{
public int TaskId { set; get; }
public DateTime ITime { set; get; }
public ManualResetEvent manualResetEvent { set; get; }
}
QueryTaskB()在檢索任務記錄數後會記錄任務條數,並實例化對應的ManualResetEvent數組,做為參數傳給線程池中線程任務。
最後等待線程池中所有任務執行完成。後續可根據實際需要編寫各自業務邏輯。
兩個實例的不同點:是否等待線程池中的所有任務完成。
實例1中定時任務對執行的時間要求比較高,到了某個時間點必須執行某個任務。所以不能等待線程池中的任務完成。
缺點:有任務就插入線程池,有些任務可能會執行很久,線程池每隔30秒循環一次,最後會導致線程池中有存在很多耗時很長的任務在線程池中未執行完。當線程池中線程數達到1023(線程池默認最大線程數)後線程池就不會在創建新的線程數去完成新的任務,只能等待當前線程池中線程數得到釋放。
實例2中數據推送程序對推送的時間要求相對1中要低一點。接口表中只要檢索到數據就推送給第三方平台。一般每次傳輸數據量不是很多,但很頻繁。
缺點:當推送數據量大時,執行任務時間可能會較長。主線程會等待線程池中的所有任務完成。所有每次循環檢索任務的時間間隔可能會出現30S+NS現象。