簡體   English   中英

SpinWait.SpinUntil的替代品

[英]Alternative to SpinWait.SpinUntil

我們在我們的應用程序中使用Task。 在一個類中,我們希望觸發在並行任務上運行的更新。 電話看起來像:

            Maintenance.RecievedMessage += new NotificationHandler(Maintenance_RecievedMessage);
            Maintenance.checkLastXML = false;
            Maintenance.NeedToUpdateFromCarrier(userId);

        SpinWait.SpinUntil(() => isCompleted == true);
        return true;

所以我們掛鈎了一個在Maintenance.NeedToUpdateFromCarrier(userId); 方法運行完畢。 完整的方法如下:

 private void Maintenance_RecievedMessage(IsCompleted changeargs)
    {
        isCompleted = true;
    }

所以我們正在等待NeedToUpdateFromCarrier方法,一旦它完成就觸發它完成的事件,我們捕獲事件並將屬性isComplete設置為true,這就是當SpinWait.SpinUntil完成時,我們繼續。

由於SpinWait.SpinUntil對CPU非常重,我現在正在尋找這個問題的替代解決方案。

重要的是要了解何時旋轉等待是合適的。 它的情況非常少。 旋轉等待優化線程上下文切換。 無論何時等待某事,WaitHandle.Wait()之類的調用都會阻塞線程並產生處理器。 當操作系統找到一些其他線程來執行有用的工作時,它會執行線程上下文切換。

線程上下文切換非常昂貴。 沒有確切的數字,因為它取決於yield-to thread運行的位置,當該線程在另一個進程或保護環(驅動程序)中運行時會產生額外的開銷。 它的成本在2000到10,000個周期之間。

那些是cpu周期,並沒有取得多大成就。 只是開銷沒有完成真正的工作。 如果您知道等待條件總是需要少於20,000個周期,則可以優化線程代碼。 只是延遲你的線程(旋轉)將確保不需要昂貴的上下文切換。 這不是像Thread.Sleep()這樣的正常延遲,它會產生,它是一個小循環,可以燃燒100%核心。 拋出一些聰明的東西,就像在只有一個核心的機器上旋轉一樣,永遠不會有效,所以它無論如何都會產生效果。

顯然,如果等待條件持續超過20,000個周期,這將無法正常工作。 現在你處於明智選擇的另一端,你確實希望在那些情況下屈服。 不僅僅是為了避免在沒有完成任何事情時燒掉cpu,尤其是因為屈服使得現在更有可能更快地滿足等待條件。 因為增加設置等待條件的線程可以獲得足夠的cpu周期來完成其工作的幾率。

有很多證據表明你的代碼就是這種情況。 您明確要求代碼在旋轉之前執行某些操作。 它需要一個事件處理程序來指示完成。 Mucho代碼需要運行。 最令人信服的是,你看到很多cpu被燒毀了。 TaskMgr.exe中1%的負載大約是2000萬個cpu周期。

請使用等待事件,例如AutoResetEvent。 注意所需的結構變化,isCompleted不再是bool。 你在完成處理程序中調用Set(),Wait()來阻止它。

您可以使用ManualResetEventSlim

var signal = new ManualResetEventSlim();

Maintenance.RecievedMessage += delegate { signal.Set(); };
Maintenance.checkLastXML = false;
Maintenance.NeedToUpdateFromCarrier(userId);

signal.Wait();
return true;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM