繁体   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