-
忙碌等待
鎖定
- 中文名
- 忙碌等待
- 外文名
- Busy waiting、busy-looping
- 又 稱
- 自旋
- 領 域
- 軟件工程
忙碌等待簡介
忙碌等待是進程反覆檢查一個條件是否為真的技術,如鍵盤輸入是否可用。忙碌等待也可以用來生成一個任意的時間延遲,這一技術對於一無法產生特定時間長度方法的系統是必要的。從計算機間的傳輸速度和計算機處理器速度差異很大,特別是一些處理器根據外部因素設計動態調整速度,如操作系統上的負載。因此,忙碌等待延遲技術往往產生不可預知的甚至不一致的結果,除非編程來確定如何確定處理器可以執行“無所作為”的速度。
對於多核CPU,忙碌等待的優點是不切換線程,避免了由此付出的代價。因此一些多線程同步機制不使用切換到內核態的同步對象,而是以用户態的自旋鎖或其派生機制(如輕型讀寫鎖)來做同步,付出的時間複雜度相差3個數量級。忙碌等待可使用一些機制來降低CPU功耗,如Windows系統中調用YieldProcessor,實際上是調用了SIMD指令_mm_pause。
[2]
忙碌等待C語言的示例程序
#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h> volatile int i = 0; /* i is global, so it is visible to all functions. It's also marked volatile, because it may change in a way which is not predictable by the compiler, here from a different thread. */ /* f1 uses a spinlock to wait for i to change from 0. */ static void *f1(void *p) { while (i == 0) { /* do nothing - just keep checking over and over */ } printf("i's value has changed to %d.\n", i); return NULL; } static void *f2(void *p) { sleep(60); /* sleep for 60 seconds */ i = 99; printf("t2 has changed the value of i to %d.\n", i); return NULL; } int main() { int rc; pthread_t t1, t2; rc = pthread_create(&t1, NULL, f1, NULL); if (rc != 0) { fprintf(stderr, "pthread f1 failed\n"); return EXIT_FAILURE; } rc = pthread_create(&t2, NULL, f2, NULL); if (rc != 0) { fprintf(stderr, "pthread f2 failed\n"); return EXIT_FAILURE; } pthread_join(t1, NULL); pthread_join(t2, NULL); puts("All pthreads finished."); return 0; }
上述的程序也可以用C11標準中的條件變量達成。
忙碌等待忙碌等待的替代品
大多數操作系統和線程庫提供了各種各樣可以阻止事件過程的系統調用,如鎖獲取、計時器變化,I/O可用性或信號。使用系統調用來產生延遲會有最簡單、最有效、公平且沒有競爭危害的結果。一個調用會檢查、通知事件等待的調度程序,插入一個適用的記憶障礙,也可以在返回之前執行所請求的I / O操作。當調用者被堵住時,其他進程可以使用CPU。調度器有實現優先級繼承所需的信息或其他機制,來避免資源衰竭的問題。
在大部分操作系統中,也可以在忙碌等待中加入延遲函數(sleep()),以減少忙碌等待浪費的CPU資源。這可以讓線程暫停指定的時間,在此期間線程不會浪費CPU時間。如果循環檢查只是檢查一些簡單的事務,將大部分時間花費在延遲函數,則不太會浪費太多CPU時間。
若程序永遠不會結束(如操作系統),可以通過無條件跳轉(例如NASM語法中的jmp $)實現無限次的忙碌等待。CPU會永遠無條件跳轉到程序正在運行到的位置。因此可以用以下的程序取代忙碌等待:
sleep: hlt jmp sleep