簡體   English   中英

UI 被性能計數器阻止

[英]UI is being blocked by Performance counters

所以我有一個按鈕、一個面板和一個用戶控件,單擊按鈕時,用戶控件被調用並顯示在面板上,問題是由於性能計數器,用戶控件需要大約 4-5 秒才能顯示。

usercontrol 包含一個計時器,計時器只是更新兩個標簽(以顯示 cpu 和 ram 使用情況)

private void UpdateUI_Tick(object sender, EventArgs e)
{
    Task.Run(() => {
        LabelCPU.Text = $"CPU : {(int)PerformanceCounterCPU.NextValue()}%";
        LabelMemory.Text = $"Memory : {(int)PerformanceCounterMemory.NextValue()}%";
    });
}

我什么都試過了,但它一直在凍結。

用戶控件包含以下代碼:

private static UserControlLogs _instance;
public static UserControlLogs Instance
{
    get
    {
        if (_instance == null || _instance.IsDisposed)
            _instance = new UserControlLogs();
        return _instance;
    }
}

調用用戶控件的按鈕代碼:

if (!PanelMain.Controls.Contains(UserControls.UserControlLogs.Instance))
{
    PanelMain.Controls.Add(UserControls.UserControlLogs.Instance);
    uc.Dock = DockStyle.Fill;
    uc.BringToFront();
}
else
{
    uc.BringToFront();
}

根據我們的談話,即使在應用了各種策略之后,您仍然會遇到系統凍結。 重新啟用此答案的目的是顯示到目前為止已嘗試過的所有內容。 這樣,如果其他人想嘗試一下,他們將能夠開辟新天地。


  1. 重寫UserControlOnVisibleChanged以進行初始化。 啟動一個任務以在第一次出現 Visible 時創建 PerformanceCounters(在它發生時旋轉 WaitCursor),然后啟動計時器。

public partial class UserControlLogs : UserControl
{
    protected override void OnVisibleChanged(EventArgs e)
    {
        base.OnVisibleChanged(e);
        if(Visible)
        {
            if(PerformanceCounterCPU == null)
            {
                Task.Run(()=>
                {
                    Parent?.Invoke((MethodInvoker)delegate { Parent.UseWaitCursor = true; });
                    Debug.WriteLine($"{UserControlLogs.Stopwatch.Elapsed}: Initializing Performance Counters");
                    UserControlLogs.Stopwatch.Restart();
                    PerformanceCounterCPU = new PerformanceCounter("Processor", "% Processor Time", "_Total");
                    PerformanceCounterCommittedMemory = new PerformanceCounter("Memory", "% Committed Bytes In Use");
                    PerformanceCounterAvailableMemory = new PerformanceCounter("Memory", "Available MBytes");
                    Debug.WriteLine($"{UserControlLogs.Stopwatch.Elapsed}: Initialization complete");
                    UserControlLogs.Stopwatch.Restart();
                    Parent?.BeginInvoke((MethodInvoker)delegate 
                    {
                        Parent.UseWaitCursor = false;
                        timerUpdateUI.Enabled = true;
                    });
                });
            }
        }
    }
}

  1. 添加信號量以阻止重入。 如果任務沒有機會完成,這將避免啟動新任務並讓它們堆積起來。

  2. 將數據的獲取與 UI 的更新分開。 PerformanceCounters 可以在后台線程上讀取。 然后只需獲取結果並將其編組回 UI 線程以顯示。

  3. 添加Stopwatch以診斷導致瓶頸的確切行。 Output Elapsed 間隔到 Debug window 來監控。


public partial class UserControlLogs : UserControl
{
    public static Stopwatch Stopwatch { get; } = new Stopwatch();
    public UserControlLogs()
    {
        InitializeComponent();
        timerUpdateUI.Enabled = false;
    }
    private PerformanceCounter PerformanceCounterCPU = null;
    private PerformanceCounter PerformanceCounterCommittedMemory = null;
    private PerformanceCounter PerformanceCounterAvailableMemory = null;

    SemaphoreSlim _noReentrancy = new SemaphoreSlim(1, 1);

    // Handler is now async
    private async void timerUpdateUI_Tick(object sender, EventArgs e)
    {
        int cpu = 0, committed = 0, available = 0;
        
        // Skip if task is still busy getting the Performance Counter from last time.
        if (_noReentrancy.Wait(0))
        {
            var cts = new CancellationTokenSource();
            var wdt = startWatchdog(TimeSpan.FromSeconds(2));
            try
            {
                _ = Task.Run(() => getPerformance());
                await wdt; 
                BeginInvoke((MethodInvoker)delegate { updateUI(); });
            }
            catch (TimeoutException ex)
            {
                Debug.WriteLine($"{DateTime.Now} {ex.Message}");
            }
            finally
            {
                _noReentrancy.Release();
            }

            void getPerformance()
            {
                Debug.WriteLine($"{UserControlLogs.Stopwatch.Elapsed}: Getting performance data");
                UserControlLogs.Stopwatch.Restart();
                cpu = (int)PerformanceCounterCPU.NextValue();
                committed = (int)PerformanceCounterCommittedMemory.NextValue();
                available = (int)PerformanceCounterAvailableMemory.NextValue();
                cts.Cancel(); // Cancel the WDT
                Debug.WriteLine($"{UserControlLogs.Stopwatch.Elapsed}: Performance data received");
                UserControlLogs.Stopwatch.Restart();
            }
            void updateUI()
            {
                Debug.WriteLine($"{UserControlLogs.Stopwatch.Elapsed}: UI Updating");
                UserControlLogs.Stopwatch.Restart();
                labelCPU.Text = $"{cpu}%";
                labelMemoryCommitted.Text = $"{committed}%";
                labelMemoryAvailable.Text = $"{available}%";

                _colorToggle = !_colorToggle;
                if(_colorToggle) BackColor = Color.LightCyan;
                else BackColor = Color.LightBlue;
                Debug.WriteLine($"{UserControlLogs.Stopwatch.Elapsed}: UI Updated");
                UserControlLogs.Stopwatch.Restart();
            }
            async Task startWatchdog(TimeSpan timeout)
            {
                try
                {
                    Debug.WriteLine($"{UserControlLogs.Stopwatch.Elapsed}: Watchdog Started");
                    UserControlLogs.Stopwatch.Restart();
                    await Task.Delay(timeout, cts.Token);
                    Debug.WriteLine($"{UserControlLogs.Stopwatch.Elapsed}: Watchdog Timed Out");
                    UserControlLogs.Stopwatch.Restart();
                    throw new TimeoutException();
                }
                catch (TaskCanceledException) 
                {
                    Debug.WriteLine($"{UserControlLogs.Stopwatch.Elapsed}: Watchdog Cancelled");
                    UserControlLogs.Stopwatch.Restart();
                }
            }
        }
    }
}

  1. 不要阻止按鈕單擊。 使用BeginInvoke顯示 UserControl。

public partial class MainForm : Form
{
    public MainForm()
    {
        UserControlLogs.Stopwatch.Start();
        Debug.WriteLine($"{UserControlLogs.Stopwatch.Elapsed}: MainForm CTor:");
        InitializeComponent();
    }

    private void buttonShowUC_Click(object sender, EventArgs e)
    {
        BeginInvoke((MethodInvoker)delegate
        {
            Debug.WriteLine($"{UserControlLogs.Stopwatch.Elapsed}: ShowUC Click:");
            UserControlLogs.Stopwatch.Restart();
            _instance.Show();
        });
    }
}

暫無
暫無

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

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