此文并不是說要完全放棄使用Thread.Sleep,而是要說明在符合哪些情況下使用!
很多時候,我們會需要一個定時服務(wù)來處理業(yè)務(wù)。
但并不是死死的每隔N分鐘執(zhí)行一次那種,而是在一次處理完后,算好下一次處理的時間點。
當?shù)竭_此時間點,觸發(fā)程序重新開始執(zhí)行代碼。
普遍的情況下,都是使用while(true){Thread.Sleep()}來實現(xiàn),廢話不多話,看代碼版本1:
class PRogram { static void Main(string[] args) { var workLists = new List<string>() { "任務(wù)1", "任務(wù)2", "任務(wù)3", "任務(wù)4" }; foreach (var task in workLists) { var thread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(Work.DoWork)); thread.Start(task); } } }
class Work { public static void DoWork(object target) { var taskType = target as string; var interval = 1 * 60 * 1000;//處理失敗,1分鐘后重試 var maxTimes = 5; var retryTimes = 0; while (true) { while (retryTimes < maxTimes) { var ok = Proccess(taskType); if (ok) { retryTimes = maxTimes; } else { retryTimes++; System.Threading.Thread.Sleep(interval); } } var tim = GetTotalMillisecondsForNext();//計算離下一次開始處理的時間 System.Threading.Thread.Sleep(tim);//掛起一段時間后,重新喚醒 retryTimes = 0; } } private static bool Proccess(string taskType) { Console.WriteLine("開始執(zhí)行處理:{0}", taskType); return true; } private static int GetTotalMillisecondsForNext() { //這里根據(jù)自己的業(yè)務(wù)來決定 return 2 * 1000; } }代碼簡單易懂。
版本1中,循環(huán)強制創(chuàng)建線程,并使用System.Threading.Thread.Sleep(tim)來掛起線程,然后重新喚醒。
這種方式不好之處在于:占用系統(tǒng)線程資源,是一種浪費。如同占著茅坑不拉屎!線程是一種十分寶貴的資源,創(chuàng)建,銷毀,切換 都是相當耗性能的。
當Sleep的時候,就等于說:現(xiàn)在我不用,但是你也別想用。你要用?自己去Create一個。
有的人說,Sleep的時候 不占用CPU啊!對,是不占用CPU ,但是占著線程資源,阻礙系統(tǒng)的線程調(diào)度!
可以參考下這文章
Threads are a limited resource, they take approximately 200,000 cycles to create and about 100,000 cycles to destroy. By default they reserve 1 megabyte of virtual memory for its stack and use 2,000-8,000 cycles for each context switch. This makes any waiting thread a huge waste.
使用System.Timers.Timer來改進我們的程序。當執(zhí)行處理業(yè)務(wù)的代碼時,首先把timer停止,處理完畢后,算好一次執(zhí)行的時間點,賦給timer并啟動,看代碼版本2
class Program { static void Main(string[] args) { var workLists = new List<string>() { "任務(wù)1", "任務(wù)2", "任務(wù)3", "任務(wù)4" }; Parallel.ForEach(workLists, new ParallelOptions() { MaxDegreeOfParallelism = 3 }, (task) => { new Work2() { TaskType = task }.DoWork(); }); Console.ReadLine(); } }
class Work2 { private Timer _workTimer; public string TaskType { get; set; } public void DoWork() { _workTimer = new System.Timers.Timer(); _workTimer.Interval = 1000; _workTimer.Elapsed += new ElapsedEventHandler(TimerHanlder); _workTimer.Start(); } private void TimerHanlder(object sender, ElapsedEventArgs e) { _workTimer.Stop(); var interval = 1 * 60 * 1000;//處理失敗,1分鐘后重試 var maxTimes = 5; var retryTimes = 0; while (retryTimes < maxTimes) { var ok = Proccess(); if (ok) { retryTimes = maxTimes; } else { retryTimes++; System.Threading.Thread.Sleep(interval); } } var times = GetTotalSecondsForNext(); Console.WriteLine("{0}秒后重新執(zhí)行", times); _workTimer.Interval = times * 1000;//計算離下一次開始處理的時間 _workTimer.Start(); } private bool Proccess() { Console.WriteLine("開始執(zhí)行處理:{0}", TaskType); return true; } private int GetTotalSecondsForNext() { //這里根據(jù)自己的業(yè)務(wù)來決定 return 3; } }
特別說明一下:Main方法中的Console.ReadLine();很重要,讓主線程處于等待的狀態(tài),子線程就可以一直執(zhí)行下去不中斷
1:使用Task,而不是使用new System.Threading.Thread。是否要創(chuàng)建線程,應(yīng)該讓系統(tǒng)來決定,利用可復(fù)用資源
2: System.Threading.Thread.Sleep(interval);只合適在 "有限度的 " 循環(huán)場景中,比如 最多重試N次、倒計時等等
如果不對之處,請各位斧正!
新聞熱點
疑難解答