[英]How to do friendly cpu loop in c#
我正在嘗試檢查布爾值是否為真,如果為假,請繼續檢查直到它為真。 我發現了這一點,但我並不真正理解這個例子。 我什么也沒找到,我試着很容易地做到這一點,但它不起作用,它很冷。
private void dashboard_Paint(object sender, PaintEventArgs e)
{
if (IsLogged().GetAwaiter().GetResult())
{
Console.WriteLine("Logged");
}
}
public async Task<Boolean> IsLogged()
{
while (!logged)
{
if (logged)
return true;
await Task.Delay(25);
}
return false;
}
C# 中的Task
是一種實現協作式多任務處理的方法,其中許多任務可能會在同一個線程上運行,每個任務在為其他任務提供 CPU 執行之前做少量工作。 如果其中一個任務行為不端並且沒有返回給任務調度程序,則線程被阻塞並且在塊被清除之前不會運行任何任務。 當再次調用任務時,它會從中斷的地方繼續。
默認任務調度程序在線程池上運行任務,這有助於減輕(但不是消除)線程阻塞的影響。 在 WinForms 上,雖然任務調度程序默認設置為在 UI 線程上運行任務,因為很多操作只能從主線程完成。 總的來說,這是一件好事,但只要出現線程阻塞,就會出現問題。 您不是阻塞一組線程池線程中的一個,而是阻塞您的 UI 和所有其他線程正在運行的線程。
處理這個問題的方法是在任何有意義的地方使用async
和await
。 如果您一直在等待任務完成,請使用await
來完成。 如果您發現自己在使用.Result
、 .Wait( .Wait()
或.GetAwaiter().GetResult()
,那么問問自己是否可以將其重寫為async
方法,即使您必須將async void
用於事件處理程序。
對於您的繪畫事件,這意味着這樣做:
private async void dashboard_Paint(object sender, PaintEventArgs e)
{
if (await IsLogged())
Console.WriteLine("Logged");
}
(雖然您通常不應該使用async void
,但這是異步事件處理程序的要求,因為事件簽名是delegate void X(...)
而不是delegate Task X(...)
。)
我不會討論為什么您永遠不應該在OnPaint
事件處理程序中等待,或者為什么您的IsLogged
示例有問題(並且可能應該改為WaitLogged
),但是您可能需要注意一件事案例: volatile
關鍵字。
假設logged
是一個字段而不是屬性,優化器可以捕獲該字段的值並在方法的生命周期內繼續使用該捕獲的值。 volatile
關鍵字告訴優化器這不應該發生,並且每次對logged
的引用都應該始終導致從變量而不是捕獲的值中讀取。
在最壞的情況下,您最終可能會得到如下所示的優化代碼:
private async Task<bool> IsLogged()
{
if (!logged)
{
while (true)
await Task.Delay(25)
}
return true;
}
從優化器的角度來看,這就是您的代碼所做的。 它不知道logged
的值可以被其他東西改變,所以它不考慮那個選項。 添加volatile
讓它知道它不能這樣做。
private async Task dashboard_Paint(object sender, PaintEventArgs e)
{
if (await IsLogged())
{
Console.WriteLine("Logged");
}
}
這應該可以解決凍結問題,因為如果使用不當,GetAwaiter() 有時會凍結。
您是否嘗試檢查將日志記錄布爾值設置為 true 的原因? 這似乎是一個無限循環。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.