轉載請注明原文地址:https://www.cnblogs.com/ygj0930/p/10843676.html
一:什么是上下文切換
CPU處理任務時不是一直只處理一個,而是通過給每個線程分配CPU時間片,時間片用完了就切換下一個線程。時間片非常短,一般只有幾十毫秒,所以CPU通過不停地切換線程執行時我們幾乎感覺不到任務的停滯,讓我們感覺是多個線程同時執行。
CPU通過時間片分配算法來循環執行任務,當前任務執行一個時間片后會切換到下一個任務。但是,在切換前會保存上一個任務的狀態,以便下次切換回這個任務時,可以再次加載這個任務的狀態,從任務保存到再加載的過程就是一次上下文切換。
這樣的切換是會影響多線程的執行效率的。
二:如何減少上下文切換來提高多線程程序的運行效率
線程的上下文切換分為讓步式上下文切換和搶占式上下文切換。
前者是指執行線程主動釋放CPU,與鎖競爭嚴重程度成正比,可通過減少鎖競爭來避免;
后者是指線程因分配的時間片用盡而被迫放棄CPU或者被其他優先級更高的線程所搶占,一般由於線程數大於CPU可用核心數引起,可通過調整線程數,適當減少線程數來避免。
1)無鎖並發編程。鎖競爭時會引起上下文切換,所以多線程處理數據時,可以用一些辦法來避免使用鎖,如將數據的ID按照Hash取模分段,不同的線程處理不同段的數據。
2)減少鎖的使用,能用CAS代替鎖時盡量用CAS代替。Java的Atomic包使用CAS算法來更新數據,而不需要加鎖。
在多線程競爭下,加鎖、釋放鎖會導致比較多的上下文切換和調度延時,引起性能問題。
多個線程使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試(只要cpu分配給線程的時間片沒有過,就可以不斷的重試,但是時間片過后,如果還是沒有成功,也會進行上下文切換,所以說只是減少了上下文切換)。
3)使用最少線程。避免創建不需要的線程,比如任務很少,但是創建了很多線程來處理,這樣會造成大量線程都處於等待狀態。
4)使用協程。在單線程里實現多任務的調度,並在單線程里維持多個任務間的切換。