UI正被性能计数器阻止



所以我有一个按钮,一个面板和一个用户控制,点击该按钮,用户控制被调用并显示在面板上,问题是由于性能计数器的原因,usercontrol显示大约需要4-5秒。

用户控件包含一个计时器,计时器只需更新两个标签(显示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以诊断导致瓶颈的确切线路。将运行时间间隔输出到要监视的"调试"窗口


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();
});
}
}

最新更新