我有一個 WPF 應用程式,它需要處理大量資料并將其作為影像可視化到螢屏上。當然,如果我從 UI 執行緒執行此操作,它會凍結,因此我將其卸載到執行緒池。在這里,當我在附加除錯器的情況下啟動它時,它運行良好且沒有卡頓。但是,如果我在沒有附加除錯器的情況下啟動它,我不明白為什么它會卡頓很多。
一般來說,沒有附加除錯器啟動的程式應該運行得更快,對吧?那么為什么在我的應用程式中它只會讓情況變得更糟呢?將配置設定為 Debug 或 Release 都無濟于事,這里重要的是使用或不使用除錯器啟動它。
由于代碼太復雜,無法在此處顯示,而且我不知道如何為我的特定問題做最少的可重現示例代碼,這里是專案問題的存盤庫鏈接,您可以在使用/不使用除錯器啟動時看到 GIF,這是一個公共回購: https ://github.com/binstarjs03/AerialOBJ/issues/2
我的設定是在 .NET 6.0.400、Visual Studio 2022 17.3.1、Windows 10 64bit
更多背景關系:
我的 UI 執行緒將其 CPU 密集型作業卸載到我的所有 CPU 最大執行緒,即 12,如果您認為問題可能是并發問題(例如滿足鎖等),我認為這不是問題,因為它是即發即棄,完全不涉及鎖定。執行緒之間的唯一通信是當后臺作業人員完成其任務時,它告訴 UI 執行緒更新影像。
另一個可能的罪魁禍首可能是GC。該程式每秒大約觸發 20 次 GC,但回到主要問題,它僅在附加除錯器的情況下才能順利啟動,而在沒有除錯器的情況下會卡頓。
編輯:
經過進一步調查,我發現在后臺作業執行緒方法中,我禁用了一種正是后臺作業執行緒應該執行的方法,它消除了口吃。同時我發現該方法是 GC 壓力背后的罪魁禍首。
現在我們可以縮小問題的范圍,這是一個GC壓力問題。正如我上面提到的,如果后臺作業執行緒呼叫它(乘以 CPU 內核數),GC 每秒會啟動大約 20 次。這個特定方法的作用是將位元組陣列反序列化為素數,包括字串。我通過創建Span<byte>而不是優化了決議器,new byte[]但盡管如此,我不知道我的方法究竟在哪里生成了很多堆。當然,反序列化的資料存盤在某個地方,例如串列,因此 GC 無法回收它,因此 GC 壓力的真正罪魁禍首的確切位置仍然未知。
示例我的意思Span而不是new byte[](將位元組陣列反序列化為以長度為前綴的字串):
public string ReadStringLengthPrefixed(bool isBigEndian)
{
ushort length = ReadUShort(isBigEndian); // here endiannes matter
Span<byte> bytes = length < 1024 ? stackalloc byte[length] : new byte[length];
if (Read(bytes) != length)
throw new EndOfStreamException();
return Encoding.UTF8.GetString(bytes);
}
很多觸發GC的方法反序列化的相關檔案和行: https ://github.com/binstarjs03/AerialOBJ/blob/main/src/binstarjs03.AerialOBJ.Core/BinaryReaderEndian.cs https://github.com /binstarjs03/AerialOBJ/blob/6a5bb01b09b45bd7b366e73022000c39b375989c/src/binstarjs03.AerialOBJ.Core/MinecraftWorld/Region.cs#L193
uj5u.com熱心網友回復:
傳統上,應用程式中的 UI 重繪 周期很高,我們希望通過游戲設計來尋找解決方案。
游戲世界中的通用解決方案是使用計時器定期執行渲染邏輯,而不是while(true). 調整回圈的頻率,以便 UI 有足夠的時間在下一個請求開始之前呈現上一個請求。
- 對于實時應用程式,這通常看起來像兩個回圈,一個處理盡可能快,另一個以較低頻率運行以更新 UI。
計算頻率有一個實際限制,當渲染結果的時間比計算你得到的競爭條件的值所花費的時間長時,這些競爭條件在視覺上就像你的例子一樣。
while(true)樣式回圈非常容易受到此影響。在 WPF 中,您的視覺更新正在排隊,如果您溢位緩沖區的速度快于可以呈現結果的速度,則渲染器最終會中止緩沖區中的一些幀以嘗試趕上。
- 如果您仔細查看生產版本,畫布的回應速度更快,并且加載的塊比除錯版本更多,實際上它的處理速度更快。
通過更高頻率的處理,您會看到更多的 GC 活動,但這并不意味著 GC導致了問題,它只是在做這件事來保持一切順利運行。如果您不期望一個高頻程序,那么您可能會擔心 GC,但否則這是預期的行為。
- 在除錯模式下運行時,您的計算會受到有效限制,導致計算時間比在發布模式下要長,因此更難觀察到這種現象。
您可能很想在處理回圈中添加故意延遲而不是計時器...不要這樣做,這是一種反模式,通常會導致執行緒的額外阻塞而不是釋放執行緒來完成作業.
- 請使用定時器,這是一個經典的定時器實作場景。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/532972.html
標籤:C#wpf表现垃圾收集
上一篇:自定義Validation.ErrorTemplate不顯示
下一篇:在WPF中裁剪旋轉的物件
