简体   繁体   English

UI 被性能计数器阻止

[英]UI is being blocked by Performance counters

so I have a button, a panel and a usercontrol , on a click of the button the usercontrol is being called and shown on the panel, the problem is that it takes around 4-5 seconds for the usercontrol to show due to performance counters.所以我有一个按钮、一个面板和一个用户控件,单击按钮时,用户控件被调用并显示在面板上,问题是由于性能计数器,用户控件需要大约 4-5 秒才能显示。

the usercontrol contains a timer, the timer simply updates the two labels (to show cpu and ram usage) 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()}%";
    });
}

I tried everything but it just keeps on freezing.我什么都试过了,但它一直在冻结。

the usercontrol contains this code:用户控件包含以下代码:

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

the code of the button that calls the usercontrol:调用用户控件的按钮代码:

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

Based on our conversation, even after applying various strategies you are still experiencing system freezes.根据我们的谈话,即使在应用了各种策略之后,您仍然会遇到系统冻结。 The purpose of re-enabling this answer is to show all the things that have been tried so far.重新启用此答案的目的是显示到目前为止已尝试过的所有内容。 This way, if anyone else wants to take a crack at it they'll be able to cover new ground.这样,如果其他人想尝试一下,他们将能够开辟新天地。


  1. Override the OnVisibleChanged of the UserControl to initialize.重写UserControlOnVisibleChanged以进行初始化。 Start a Task to create PerformanceCounters on the first occurrence of Visible (spin a WaitCursor while it's taking place) and then start the timer.启动一个任务以在第一次出现 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. Add a semaphore to block reentrancy.添加信号量以阻止重入。 If the Task hasn't had a chance to complete, this will avoid starting new ones and having them pile up.如果任务没有机会完成,这将避免启动新任务并让它们堆积起来。

  2. Make the acquisition of data separate from the updating of the UI.将数据的获取与 UI 的更新分开。 The PerformanceCounters can be read on a background thread. PerformanceCounters 可以在后台线程上读取。 Then just take the result and marshal it back onto the UI thread to display.然后只需获取结果并将其编组回 UI 线程以显示。

  3. Add Stopwatch to diagnose the exact lines that are causing bottlenecks.添加Stopwatch以诊断导致瓶颈的确切行。 Output the Elapsed intervals to the Debug window to monitor. 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. Don't block the button click.不要阻止按钮单击。 Use a BeginInvoke to show the UserControl.使用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