此文并不是說(shuō)要完全放棄使用Thread.Sleep,而是要說(shuō)明在符合哪些情況下使用!
很多時(shí)候,我們會(huì)需要一個(gè)定時(shí)服務(wù)來(lái)處理業(yè)務(wù)。
但并不是死死的每隔N分鐘執(zhí)行一次那種,而是在一次處理完后,算好下一次處理的時(shí)間點(diǎn)。
當(dāng)?shù)竭_(dá)此時(shí)間點(diǎn),觸發(fā)程序重新開始執(zhí)行代碼。
普遍的情況下,都是使用while(true){Thread.Sleep()}來(lái)實(shí)現(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();//計(jì)算離下一次開始處理的時(shí)間 System.Threading.Thread.Sleep(tim);//掛起一段時(shí)間后,重新喚醒 retryTimes = 0; } } private static bool Proccess(string taskType) { Console.WriteLine("開始執(zhí)行處理:{0}", taskType); return true; } private static int GetTotalMillisecondsForNext() { //這里根據(jù)自己的業(yè)務(wù)來(lái)決定 return 2 * 1000; }}
代碼簡(jiǎn)單易懂。
版本1中,循環(huán)強(qiáng)制創(chuàng)建線程,并使用System.Threading.Thread.Sleep(tim)來(lái)掛起線程,然后重新喚醒。
這種方式不好之處在于:占用系統(tǒng)線程資源,是一種浪費(fèi)。如同占著茅坑不拉屎!線程是一種十分寶貴的資源,創(chuàng)建,銷毀,切換 都是相當(dāng)耗性能的。
當(dāng)Sleep的時(shí)候,就等于說(shuō):現(xiàn)在我不用,但是你也別想用。你要用?自己去Create一個(gè)。
有的人說(shuō),Sleep的時(shí)候 不占用CPU啊!對(duì),是不占用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來(lái)改進(jìn)我們的程序。當(dāng)執(zhí)行處理業(yè)務(wù)的代碼時(shí),首先把timer停止,處理完畢后,算好一次執(zhí)行的時(shí)間點(diǎn),賦給timer并啟動(dòng),看代碼版本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;//計(jì)算離下一次開始處理的時(shí)間 _workTimer.Start(); } private bool Proccess() { Console.WriteLine("開始執(zhí)行處理:{0}", TaskType); return true; } private int GetTotalSecondsForNext() { //這里根據(jù)自己的業(yè)務(wù)來(lái)決定 return 3; } }
特別說(shuō)明一下:Main方法中的Console.ReadLine();很重要,讓主線程處于等待的狀態(tài),子線程就可以一直執(zhí)行下去不中斷
1:使用Task,而不是使用new System.Threading.Thread。是否要?jiǎng)?chuàng)建線程,應(yīng)該讓系統(tǒng)來(lái)決定,利用可復(fù)用資源
2: System.Threading.Thread.Sleep(interval);只合適在 "有限度的 " 循環(huán)場(chǎng)景中,比如 最多重試N次、倒計(jì)時(shí)等等
新聞熱點(diǎn)
疑難解答
圖片精選