c# - 应用程序挂起使用 WMI 读取当前 CPU ClockSpeed
问题描述
我从一个基本的 Winforms 应用程序开始,每三秒获取一次当前的 CPU 时钟速度,并用检索到的值刷新一个标签。
起初我使用了一个线程定时器,但我读到它是“不好的做法”,因为它消耗了一个线程。所以,我使用 BackgroundWorker 类来完成这项工作。不幸的是,该程序仍然每三秒冻结一次。
特此一些代码片段:
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 10; i++) {
GenerateDelay(3000);
backgroundWorker1.ReportProgress(i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
GetCurrentCpuSpeed();
}
private void GetCurrentCpuSpeed()
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Processor");
foreach (ManagementObject obj in searcher.Get()) {
var clockSpeed = obj["CurrentClockSpeed"].ToString();
lblCurrentClockSpeed.Text = clockSpeed;
}
}
private void GenerateDelay(int miliseconds)
{
Thread.Sleep(miliseconds);
}
我也尝试过这种方法作为一种变体:
private void GetCurrentCpuClockSpeed()
{
using (ManagementClass mc = new ManagementClass("win32_processor"))
{
var instances = mc.GetInstances();
foreach (var item in instances) {
try {
lblCurrentClockSpeed.Text = item.Properties["CurrentClockSpeed"].Value.ToString();
}
catch (Exception ex) {
throw;
}
}
}
}
编辑:根据评论更改代码以使用计时器。删除了我的“答案帖”。
以下是一些代码片段:
private void Form1_Load(object sender, EventArgs e)
{
CreateAndStartTimer();
}
private void CreateAndStartTimer()
{
System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
t.Interval = 3000;
t.Tick += new EventHandler(GetCurrentCpuSpeed2);
t.Start();
}
private void GetCurrentCpuSpeed2(object sender, EventArgs e)
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Processor");
foreach (ManagementObject obj in searcher.Get()) {
var clockSpeed = obj["CurrentClockSpeed"].ToString();
lblCurrentClockSpeed.Text = "";
lblCurrentClockSpeed.Text = clockSpeed;
}
}
编辑2:
我让它工作了:)
特此我的代码片段:
private void Form1_Load(object sender, EventArgs e)
{
CreateAndStartTimer();
}
private async void CreateAndStartTimer()
{
// Create an AutoResetEvent to signal the timeout threshold in the
// timer callback has been reached.
var autoEvent = new AutoResetEvent(false);
await Task.Run(() => new System.Threading.Timer(GetCurrentCpuClockSpeed, autoEvent, 1000, 1000));
}
private void GetCurrentCpuClockSpeed(object sender)
{
bool isCallerOnDifferentThread = lblCurrentClockSpeed.InvokeRequired;
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Processor");
foreach (ManagementObject obj in searcher.Get()) {
var clockSpeed = obj["CurrentClockSpeed"].ToString();
if (isCallerOnDifferentThread) {
lblCurrentClockSpeed.Invoke(new Action(() => { lblCurrentClockSpeed.Text = clockSpeed; }));
}
}
}
解决方案
我测试了Microsoft.Management.Infrastructure(又名 WMI V2,您需要安装NuGet Package),而不是System.Management(您仍然可以使用它,但速度较慢),仅查询Win32_ProcessorCurrentClockSpeed
。
我认为返回查询值所需的时间对于使用非线程计时器(此处为System.Windows.Forms.Timer)是可以接受的。
在一台不太出色的测试机器(I5-4690K,16GB,无 SSD)中,查询返回0
到27
毫秒。
试一试:
这里,lblCpuClock
是分配给接收 CPU 时钟值的标签的名称。
这里,Timer 在 Form 构造函数中初始化,在 Form 显示时启动,在 Form 关闭时停止。
如果您需要在其他时间启动它,请不要使用 Lambda 订阅Tick
事件 ( cpuClockTimer.Tick += (s, e) => { ... }
),使用适当的方法处理程序并在停止 Timer 时取消订阅事件。
public partial class SomeForm : Form
{
System.Windows.Forms.Timer cpuClockTimer = null;
public SomeForm()
{
InitializeComponent();
// [...]
if (this.components == null) this.components = new Container();
cpuClockTimer = new System.Windows.Forms.Timer(this.components) { Interval = 1000 };
cpuClockTimer.Tick += (s, e) => {
uint? result = InfrastructureQueries.GetCurrentCpuClock(null);
if (result.HasValue && !lblCpuClock.IsDisposed) {
lblCpuClock.Text = $"Clock Speed: {result.Value} MHz";
}
};
}
protected override void OnShown(EventArgs e) {
base.OnShown(e);
cpuClockTimer.Enabled = true;
}
protected override void OnFormClosing(FormClosingEventArgs e) {
cpuClockTimer.Enabled = false;
base.OnFormClosing(e);
}
}
否则,尝试启动一个任务,看看它是否更合适。
如前所述(在现在处于已删除状态的评论中),您可以使用 BackgroundWorker,但当然,不要从执行查询的方法中引用在 UI 线程中创建的控件;该方法只需要返回一个值,然后您可以ProgressChanged
在引发事件时将其传递给事件处理程序。
该事件在 UI 线程中引发,因此您可以从处理程序安全地访问您的控件。
在这里,任务在显示表单时运行(在OnShown
覆盖范围内)。如果您想按需运行它,请使用另
一种方法。Task.Run(() => UpdateCpuClock(...)
用于ctsCpuClock.Cancel();
随时取消任务。
public partial class SomeForm : Form
{
static CancellationTokenSource ctsCpuClock = null;
Progress<uint> ClockProgress = null;
protected override void OnShown(EventArgs e) {
base.OnShown(e);
ctsCpuClock = new CancellationTokenSource();
clockProgress = new Progress<uint>(value => {
if (!lblCpuClock.IsDisposed) {
lblCpuClock.Text = $"Clock Speed: {value} MHz";
}
});
Task.Run(() => UpdateCpuClock(clockProgress, ctsCpuClock.Token));
}
protected override void OnFormClosing(FormClosingEventArgs e) {
ctsCpuClock?.Cancel();
ctsCpuClock?.Dispose();
base.OnFormClosing(e);
}
private async Task UpdateCpuClock(IProgress<uint> progress, CancellationToken token) {
while (!token.IsCancellationRequested) {
uint? result = InfrastructureQueries.GetCurrentCpuClock(null);
if (result.HasValue) {
progress.Report(result.Value);
}
try { await Task.Delay(1000, token); }
// Handle if you need logging, otherwise let go: your code called Cancel()
catch (TaskCanceledException) { break; }
}
}
}
Management.Infrastructure
工人方法:
using System.Collections.Generic;
using System.Linq;
using Microsoft.Management.Infrastructure;
public class InfrastructureQueries
{
private const string nameSpace = @"root\cimv2";
private static CimSession CreateSession(string computerName)
{
if (string.IsNullOrEmpty(computerName) ||
computerName.Equals("localhost", StringComparison.InvariantCultureIgnoreCase)) {
computerName = null;
}
return CimSession.Create(computerName);
}
public static uint? GetCurrentCpuClock(string computerName = null)
{
var session = CreateSession(computerName);
IEnumerable<CimInstance> query = session.QueryInstances(
nameSpace, "WQL", "SELECT CurrentClockSpeed FROM Win32_Processor");
return (uint?)query.FirstOrDefault()?.CimInstanceProperties["CurrentClockSpeed"].Value;
}
}
推荐阅读
- javascript - SailsJS 版本 ^1 需要将查询的区分大小写设置为 false
- matlab - 我可以在 App 设计工具中格式化工具提示字符串吗?
- c# - WebApplicationFactory 中 .NET Core 2.1 和 3.1 之间对 ConfigureServices 的调用顺序不同
- javascript - 我们如何在条形图的条形图上添加文本?
- node.js - 如何在单个液滴上将不同的 MERN 应用程序部署到数字海洋?
- java - Resources$NotFoundException: 字符串资源 ID #0x0
- powershell - 非英文系统上的New-SMBShare
- java - 如何安全地将 java Object 转换为泛型集合?
- javascript - 在 CoreUI 的 CBreadcrumbRouter 中动态更改页面标题
- vue.js - Axios for Vue 无需安装