gpt4 book ai didi

C# WPF 线程

转载 作者:太空宇宙 更新时间:2023-11-03 18:38:25 25 4
gpt4 key购买 nike

我一直在关注 this WPF 线程模型指南,创建一个小应用程序来监视和显示当前和峰值 CPU 使用率。但是,当我在事件处理程序中更新当前和峰值 CPU 时,窗口中的数字根本没有改变。调试时,我可以看到文本字段确实发生了变化,但没有在窗口中更新。

我听说构建这样的应用程序是不好的做法,应该改用 MVVM 方法。事实上,一些人对我能够在没有运行时异常的情况下运行它感到惊讶。无论如何,我想从第一个链接中找出代码示例/指南。

让我知道你的想法!

这是我的 xaml:

<Window x:Class="UsagePeak2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CPU Peak" Height="75" Width="260">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" >
<Button Content="Start"
Click="StartOrStop"
Name="startStopButton"
Margin="5,0,5,0"
/>
<TextBlock Margin="10,5,0,0">Peak:</TextBlock>
<TextBlock Name="CPUPeak" Margin="4,5,0,0">0</TextBlock>
<TextBlock Margin="10,5,0,0">Current:</TextBlock>
<TextBlock Name="CurrentCPUPeak" Margin="4,5,0,0">0</TextBlock>
</StackPanel>

这是我的代码

public partial class MainWindow : Window
{
public delegate void NextCPUPeakDelegate();

double thisCPUPeak = 0;

private bool continueCalculating = false;

PerformanceCounter cpuCounter;

public MainWindow() : base()
{
InitializeComponent();
}

private void StartOrStop(object sender, EventArgs e)
{
if (continueCalculating)
{
continueCalculating = false;
startStopButton.Content = "Resume";
}
else
{
continueCalculating = true;
startStopButton.Content = "Stop";
startStopButton.Dispatcher.BeginInvoke(
DispatcherPriority.Normal, new NextCPUPeakDelegate(GetNextPeak));
//GetNextPeak();
}
}

private void GetNextPeak()
{

cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");

double currentValue = cpuCounter.NextValue();

CurrentCPUPeak.Text = Convert.ToDouble(currentValue).ToString();

if (currentValue > thisCPUPeak)
{
thisCPUPeak = currentValue;
CPUPeak.Text = thisCPUPeak.ToString();
}

if (continueCalculating)
{
startStopButton.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.SystemIdle,
new NextCPUPeakDelegate(this.GetNextPeak));
}
}
}

最佳答案

这里有一些问题。首先,您在主线程上工作,但是您是以一种非常迂回的方式进行的。正是此处的这一行允许您的代码响应:

startStopButton.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.SystemIdle,
new NextCPUPeakDelegate(this.GetNextPeak));

来自BeginInvoke method documentation (强调我的):

Executes the specified delegate asynchronously at the specified priority on the thread the Dispatcher is associated with.

即使您以高速率发送此消息,您也会通过在后台线程上将帖子返回到消息循环中来对 UI 线程上的工作进行排队。这就是让您的 UI 完全响应的原因。

就是说,您想要像这样重构您的 GetNextPeak 方法:

private Task GetNextPeakAsync(CancellationToken token)
{
// Start in a new task.
return Task.Factory.StartNew(() => {
// Store the counter outside of the loop.
var cpuCounter =
new PerformanceCounter("Processor", "% Processor Time", "_Total");

// Cycle while there is no cancellation.
while (!token.IsCancellationRequested)
{
// Wait before getting the next value.
Thread.Sleep(1000);

// Get the next value.
double currentValue = cpuCounter.NextValue();

if (currentValue > thisCPUPeak)
{
thisCPUPeak = currentValue;
}

// The action to perform.
Action<double, double> a = (cv, p) => {
CurrentCPUPeak.Text = cv.ToString();
CPUPeak.Text = p.ToString();
};

startStopButton.Dispatcher.Invoke(a,
new object[] { currentValue, thisCPUPeak });
}
}, TaskCreationOptions.LongRunning);
}

关于上面的注释:

  • 调用NextValue methodPerformanceCounter class 上根据 Dylan's answer,在值开始通过之前必须有一些时间到期.

  • A CancellationToken structure用于指示是否应停止操作。这会驱动将在后台持续运行的循环。

  • 您的方法现在返回 Task class代表背景信息。

  • 围绕 thisCPUPeak 值进行同步是不需要的,因为单个后台线程是唯一它被读取和读取的地方写给;调用 Invoke methodDispatcher class 上有传递给它的 currentValuethisCPUPeak 值的副本。如果您想在任何 其他线程(包括 UI 线程)中访问 thisCPUPeak 值,那么您需要同步对该值的访问(很可能通过 lock statement )。

现在,您还必须在类级别上保留 Task 并引用 CancellationTokenSource (生成 CancellationToken):

private Task monitorTask = null;
private CancellationTokenSource cancellationTokenSource = null;

然后更改您的StartOrStop 方法来调用任务

private void StartOrStop(object sender, EventArgs e)
{
// If there is a task, then stop it.
if (task != null)
{
// Dispose of the source when done.
using (cancellationTokenSource)
{
// Cancel.
cancellationTokenSource.Cancel();
}

// Set values to null.
task = null;
cancellationTokenSource = null;

// Update UI.
startStopButton.Content = "Resume";
}
else
{
// Update UI.
startStopButton.Content = "Stop";

// Create the cancellation token source, and
// pass the token in when starting the task.
cancellationTokenSource = new CancellationTokenSource();
task = GetNextPeakAsync(cancellationTokenSource.Token);
}
}

请注意,它检查的不是标志,而是Task 是否已经在运行;如果没有 Task,则它启动循环,否则,它使用 CancellationTokenSource 取消现有循环。

关于C# WPF 线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11870298/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com