【问题标题】:Application hangs using WMI to read the current CPU ClockSpeed应用程序挂起使用 WMI 读取当前 CPU ClockSpeed
【发布时间】:2020-07-04 16:45:45
【问题描述】:

我从一个基本的 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;
            }
        }
    }
}

编辑:根据 cmets 更改代码以使用 Timer。删除了我的“回复帖”。

以下是一些代码片段:

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

【问题讨论】:

  • 这是使用计时器的一个非常复杂的替代方案...您的方法消耗单个线程,计时器不消耗。
  • @canton7:感谢您的回复!我认为 Backgroundworker 在单独的线程上工作,所以我不会“伤害”用户界面。这就是我选择这种方法的原因。
  • backgroundWorker1_DoWork 在单独的线程上调用,但它所做的只是让该线程休眠并每隔一段时间调用ReportProgressGetCurrentCpuSpeed 仍然在主线程上调用。调用 GetCurrentCpuSpeed 的 Timer 会做同样的事情,但不会抓住整个线程只是为了让它休眠
  • 定时器不会损害或停止 UI 线程。
  • 你可以edit你的问题包含更新的代码

标签: c# winforms wmi backgroundworker


【解决方案1】:

我测试了Microsoft.Management.Infrastructure(又名WMI V2,你需要安装NuGet Package),而不是System.Management(你也可以使用它,但是它),查询@ 987654324@ 只需 CurrentClockSpeed

我认为使用非线程定时器(System.Windows.Forms.Timer,此处)返回查询值所需的时间是可以接受的。

在不太出色的测试机器(I5-4690K,16GB,无 SSD)中,查询在 027 毫秒内返回。

试一试:

这里,lblCpuClock 是分配给接收 CPU 时钟值的标签的名称。

这里,Timer 在 Form 构造函数中初始化,在 Form 显示时启动,在 Form 关闭时停止。
如果您需要在其他时间启动它,请不要使用 Lambda 订阅 Tick 事件 (cpuClockTimer.Tick += (s, e) =&gt; { ... }),使用适当的方法处理程序并在停止 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);
    }
}

否则,尝试启动一个任务,看看它是否更合适。

如前所述(在 cmets 中现在处于 deleted 状态),您可以使用 BackgroundWorker,但当然,不要从执行的方法中引用在 UI 线程中创建的控件查询;该方法只需要返回一个值,然后您可以在引发事件时将其传递给ProgressChanged 事件处理程序。
该事件在 UI 线程中引发,因此您可以从处理程序安全地访问您的控件。

这里,当显示表单时任务运行(int OnShown 覆盖)。
如果您想按需运行,请以另一种方法移动Task.Run(() =&gt; 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;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-26
    • 1970-01-01
    相关资源
    最近更新 更多