Thread.Sleep(0) 表示掛起0毫秒,你可能以爲沒做用,你要寫Thread.Sleep(1000) 就有感受了。彷佛毫無心義。算法
MSDN的說明:指定零 (0) 以指示應掛起此線程以使其餘等待線程可以執行。併發
Thread.Sleep(0) 並不是是真的要線程掛起0毫秒,意義在於此次調用Thread.Sleep(0)的當前線程確實的被凍結了一下,讓其餘線程有機會優先執行。Thread.Sleep(0) 是你的線程暫時放棄cpu,也就是釋放一些未用的時間片給其餘線程或進程使用,就至關於一個讓位動做。
函數
Thread th = new Thread(new ThreadStart(MainForm.StartSplash)); th.Priority = ThreadPriority.AboveNormal; th.Start(); Thread.Sleep(0); base.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true); this.Initialize();
在線程中,調用sleep(0)能夠釋放cpu時間,讓線程立刻從新回到就緒隊列而非等待隊列,sleep(0)釋放當前線程所剩餘的時間片(若是有剩餘的話),這樣可讓操做系統切換其餘線程來執行,提高效率。this
咱們可能常常會用到 Thread.Sleep 函數來使線程掛起一段時間。那麼你有沒有正確的理解這個函數的用法呢?spa
思考下面這兩個問題:操作系統
操做系統中,CPU競爭有不少種策略。Unix系統使用的是時間片算法,而Windows則屬於搶佔式的。
在時間片算法中,全部的進程排成一個隊列。操做系統按照他們的順序,給每一個進程分配一段時間,即該進程容許運行的時間。若是在 時間片結束時進程還在運行,則CPU將被剝奪並分配給另外一個進程。若是進程在時間片結束前阻塞或結束,則CPU立即進行切換。調度程 序所要作的就是維護一張就緒進程列表,,當進程用完它的時間片後,它被移到隊列的末尾。
所謂搶佔式操做系統,就是說若是一個進程獲得了 CPU 時間,除非它本身放棄使用 CPU ,不然將徹底霸佔 CPU 。所以能夠看出,在搶 佔式操做系統中,操做系統假設全部的進程都是「人品很好」的,會主動退出 CPU 。在搶佔式操做系統中,假設有若干進程,操做系統會根據他們的優先級、飢餓時間(已經多長時間沒有使用過 CPU 了),給他們算出一 個總的優先級來。操做系統就會把 CPU 交給總優先級最高的這個進程。當進程執行完畢或者本身主動掛起後,操做系統就會從新計算一 次全部進程的總優先級,而後再挑一個優先級最高的把 CPU 控制權交給他。
咱們用分蛋糕的場景來描述這兩種算法。假設有源源不斷的蛋糕(源源不斷的時間),一副刀叉(一個CPU),10個等待吃蛋糕的人(10 個進程)。
若是是 Unix操做系統來負責分蛋糕,那麼他會這樣定規矩:每一個人上來吃 1 分鐘,時間到了換下一個。最後一我的吃完了就再從頭開始。因而,無論這10我的是否是優先級不一樣、飢餓程度不一樣、飯量不一樣,每一個人上來的時候均可以吃 1 分鐘。固然,若是有人原本不太餓,或者飯量小,吃了30秒鐘以後就吃飽了,那麼他能夠跟操做系統說:我已經吃飽了(掛起)。因而操做系統就會讓下一我的接着來。
若是是 Windows 操做系統來負責分蛋糕的,那麼場面就頗有意思了。他會這樣定規矩:我會根據大家的優先級、飢餓程度去給大家每一個人計算一個優先級。優先級最高的那我的,能夠上來吃蛋糕——吃到你不想吃爲止。等這我的吃完了,我再從新根據優先級、飢餓程度來計算每一個人的優先級,而後再分給優先級最高的那我的。
.net
這樣看來,這個場面就有意思了——可能有些人是PPMM,所以具備高優先級,因而她就能夠常常來吃蛋糕。可能另一我的是個醜男,而去很ws,因此優先級特別低,因而好半天了才輪到他一次(由於隨着時間的推移,他會愈來愈飢餓,所以算出來的總優先級就會愈來愈高,所以總有一天會輪到他的)。並且,若是一不當心讓一個大胖子獲得了刀叉,由於他飯量大,可能他會霸佔着蛋糕連續吃好久好久,致使旁邊的人在那裏咽口水。。。線程
並且,還可能會有這種狀況出現:操做系統如今計算出來的結果,5號PPMM總優先級最高,並且高出別人一大截。所以就叫5號來吃蛋糕。5號吃了一小會兒,以爲沒那麼餓了,因而說「我不吃了」(掛起)。所以操做系統就會從新計算全部人的優先級。由於5號剛剛吃過,所以她的飢餓程度變小了,因而總優先級變小了;而其餘人由於多等了一下子,飢餓程度都變大了,因此總優先級也變大了。不過這時候仍然有可能5號的優先級比別的都高,只不過如今只比其餘的高一點點——但她仍然是總優先級最高的啊。所以操做系統就會說:5號mm上來吃蛋糕……(5號mm內心鬱悶,這不剛吃過嘛……人家要減肥……誰叫你長那麼漂亮,得到了那麼高的優先級)。code
那麼,Thread.Sleep 函數是幹嘛的呢?還用剛纔的分蛋糕的場景來描述。上面的場景裏面,5號MM在吃了一次蛋糕以後,以爲已經有8分飽了,她以爲在將來的半個小時以內都不想再來吃蛋糕了,那麼她就會跟操做系統說:在將來的半個小時以內不要再叫我上來吃蛋糕了。這樣,操做系統在隨後的半個小時裏面從新計算全部人總優先級的時候,就會忽略5號mm。Sleep函數就是幹這事的,他告訴操做系統「在將來的多少毫秒內我不參與CPU競爭」。
看完了 Thread.Sleep 的做用,咱們再來想一想文章開頭的兩個問題。
對於第一個問題,答案是:不必定。由於你只是告訴操做系統:在將來的1000毫秒內我不想再參與到CPU競爭。那麼1000毫秒過去以後,這時候也許另一個線程正在使用CPU,那麼這時候操做系統是不會從新分配CPU的,直到那個線程掛起或結束;何況,即便這個時候恰巧輪到操做系統進行CPU 分配,那麼當前線程也不必定就是總優先級最高的那個,CPU仍是可能被其餘線程搶佔去。與此類似的,Thread有個Resume函數,是用來喚醒掛起的線程的。好像上面所說的同樣,這個函數只是「告訴操做系統我從如今起開始參與CPU競爭了」,這個函數的調用並不能立刻使得這個線程得到CPU控制權。
對於第二個問題,答案是:有,並且區別很明顯。假設咱們剛纔的分蛋糕場景裏面,有另一個PPMM 7號,她的優先級也很是很是高(由於很是很是漂亮),因此操做系統老是會叫道她來吃蛋糕。並且,7號也很是喜歡吃蛋糕,並且飯量也很大。不過,7號人品很好,她很善良,她沒吃幾口就會想:若是如今有別人比我更須要吃蛋糕,那麼我就讓給他。所以,她能夠每吃幾口就跟操做系統說:咱們來從新計算一下全部人的總優先級吧。不過,操做系統不接受這個建議——由於操做系統不提供這個接口。因而7號mm就換了個說法:「在將來的0毫秒以內不要再叫我上來吃蛋糕了」。這個指令操做系統是接受的,因而此時操做系統就會從新計算你們的總優先級——注意這個時候是連7號一塊兒計算的,由於「0毫秒已通過去了」嘛。所以若是沒有比7號更須要吃蛋糕的人出現,那麼下一次7號仍是會被叫上來吃蛋糕。
所以,Thread.Sleep(0)的做用,就是「觸發操做系統馬上從新進行一次CPU競爭」。競爭的結果也許是當前線程仍然得到CPU控制權,也許會換成別的線程得到CPU控制權。這也是咱們在大循環裏面常常會寫一句Thread.Sleep(0) ,由於這樣就給了其餘線程好比Paint線程得到CPU控制權的權力,這樣界面就不會假死在那裏。
末了說明一下,雖然上面提到說「除非它本身放棄使用 CPU ,不然將徹底霸佔 CPU」,但這個行爲仍然是受到制約的——操做系統會監控你霸佔CPU的狀況,若是發現某個線程長時間霸佔CPU,會強制使這個線程掛起,所以在實際上不會出現「一個線程一直霸佔着 CPU 不放」的狀況。至於咱們的大循環形成程序假死,並非由於這個線程一直在霸佔着CPU。實際上在這段時間操做系統已經進行過屢次CPU競爭了,只不過其餘線程在得到CPU控制權以後很短期內立刻就退出了,因而就又輪到了這個線程繼續執行循環,因而就又用了好久才被操做系統強制掛起。。。所以反應到界面上,看起來就好像這個線程一直在霸佔着CPU同樣。
末了再說明一下,文中線程、進程有點混亂,其實在Windows原理層面,CPU競爭都是線程級的,本文中把這裏的進程、線程當作同一個東西就行了。
orm
爲了等待資源、事件,那麼你須要進入等待隊列。若是你已經擁有運行所需資源,卻讓系統調度,這是資源的浪費,而且調度也是要浪費資源的
解釋:對的,你要等待資源,你確實須要排隊,假如AB兩個線程爲合做關係,A線程處理一些原始數據,數據處理到必定程度,交給B線程處理,在A處理原始數據的時候,B也要作一些準備工做,因此,AB是併發的,可是B作好準備以後,須要等待A處理好那些數據,接過A的數據,繼續處理,所以,這個等待,若是A不使用信號或者等待條件來通知B的話,那麼B必須一直輪詢,查看A是否已完成,B線程所作的這個輪詢是否會一直佔用CPU來作無用的循環查看呢?所以B這個時候佔用的cpu時間片作的是無用功,所以,這裏sleep(0)就有做用,當B查看A沒處理完數據的時候,B立刻sleep(0)交出B的時間片,讓操做系統調度A來運行(假設只有AB兩個線程),那麼這個時候,A就會獲得充分的時間來處理它的數據,這個不是一個應用了嗎?我猜想pthread_conn_wait()內部阻塞就是使用這個機制
thread_fun() { prepare_word..... while (1) { if (A is finish) break; else sleep(0); //這裏會交出B的時間片,下一次調度B的時候,接着執行這個循環 } process A's data }
沒有sleep(0)版:
thread_fun() { prepare_word..... while (1) //這裏會一直浪費CPU時間作死循環的輪詢,無用功 { if (A is finish) break; } process A's data }
若是說是輪詢,那它就是一種高效、節約、謙虛的輪詢,若是沒有sleep(0),那麼B線程可能會執行上萬次的while循環,直至它的時間片消耗完,作這些都是無用功,而是用了sleep(0)後,B線程每一次執行就只作一次while循環就把剩餘的時間片讓出給A,能讓A獲得更多的執行次數,利用率更高
在線程沒退出以前,線程有三個狀態,就緒態,運行態,等待態。sleep(n)之因此在n秒內不會參與CPU競爭,是由於,當線程調用sleep(n)的時候,線程是由運行態轉入等待態,線程被放入等待隊列中,等待定時器n秒後的中斷事件,當到達n秒計時後,線程才從新由等待態轉入就緒態,被放入就緒隊列中,等待隊列中的線程是不參與cpu競爭的,只有就緒隊列中的線程纔會參與cpu競爭,所謂的cpu調度,就是根據必定的算法(優先級,FIFO等。。。),從就緒隊列中選擇一個線程來分配cpu時間。
而sleep(0)之因此立刻回去參與cpu競爭,是由於調用sleep(0)後,由於0的緣由,線程直接回到就緒隊列,而非進入等待隊列,只要進入就緒隊列,那麼它就參與cpu競爭。