【发布时间】:2012-03-07 10:16:58
【问题描述】:
我有一个 gui 应用程序,它会定期显示 CPU 负载。负载由 StateReader 类读取:
public class StateReader
{
ManagementObjectSearcher searcher;
public StateReader()
{
ManagementScope scope = new ManagementScope("\\\\localhost\\root\\cimv2");
ObjectQuery query = new ObjectQuery("select Name,PercentProcessorTime from Win32_PerfFormattedData_PerfOS_Processor where not Name='_Total'");
searcher = new ManagementObjectSearcher(scope, query);
}
// give the maximum load over all cores
public UInt64 CPULoad()
{
List<UInt64> list = new List<UInt64>();
ManagementObjectCollection results = searcher.Get();
foreach (ManagementObject result in results)
{
list.Add((UInt64)result.Properties["PercentProcessorTime"].Value);
}
return list.Max();
}
}
使用响应式扩展更新 gui:
var gui = new GUI();
var reader = new StateReader();
var sub = Observable.Interval(TimeSpan.FromSeconds(0.5))
.Select(_ => reader.CPULoad())
.ObserveOn(gui)
.Subscribe(gui.ShowCPUState);
Application.Run(gui);
sub.Dispose();
现在当我退出我的应用程序时,我收到一条错误消息
RaceOnRCWCleanup was detected.
An attempt has been mad to free an RCW that is in use. The RCW is use on the
active thread or another thread. Attempting to free an in-use RCW can cause
corruption or data loss.
如果我不读取 cpu 负载,则不会出现此错误,而只是提供一些随机值,因此该错误以某种方式与读取负载有关。此外,如果我在Application.Run(gui) 之后放置一个断点并在那里稍等片刻,则错误似乎不会经常出现。
从这个和我的谷歌搜索来看,我认为使用管理命名空间中的类会创建一个后台线程,该线程引用包装在运行时可调用包装器中的 COM 对象,并且当我退出我的应用程序时,该线程没有时间正确关闭 RCW,导致我的错误。这是正确的,我该如何解决这个问题?
我已经编辑了我的代码以反映我得到的响应,但我仍然遇到同样的错误。代码更新了三点:
- StateReader 是 Disposable,在 Dispose 方法中释放其 ManagementObjectSearcher 在我的 main 方法中 Application.Run 之后,我在 StateReader 对象上调用 Dispose
- 在 CPULoad 中,我处理了 ManagementCollection 和其中的每个 ManagementObject
- 在我的主要方法中,我在 FormClosing 上的事件处理程序中处理订阅对象
在 gui 上。这应确保关闭后不会为 gui 生成任何事件。
代码的相关部分现在位于 StateReader 中:
// give the maximum load over all cores
public UInt64 CPULoad()
{
List<UInt64> list = new List<UInt64>();
using (ManagementObjectCollection results = searcher.Get())
{
foreach (ManagementObject result in results)
{
list.Add((UInt64)result.Properties["PercentProcessorTime"].Value);
result.Dispose();
}
}
return list.Max();
}
public void Dispose()
{
searcher.Dispose();
}
在我的主要:
gui.FormClosing += (a1, a2) => sub.Dispose();
Application.Run(gui);
reader.Dispose();
我还能做些什么来避免我得到的错误?
【问题讨论】:
-
你的诊断是正确的。这不是唯一的问题,.ObserveOn(gui) 调用也很麻烦。在您允许表单关闭之前,您必须确保不再生成通知。这就是允许线程猖獗的危害。
-
@Hans Passant:我已经编辑了代码以在 FormClosing 上处理订阅。你会说这解决了你提到的问题吗?我在表单上没有其他事件,除了来自用户与其交互的事件,例如按钮点击等。
-
可能不是,如果在调用 Dispose() 时安排了 TP 线程但尚未开始执行,则这是线程竞争。我不太了解 Reactive 管道。
-
@HansPassant:我没想到。但是想一想,这是否适用于从另一个线程调用表单上的方法的任何方案?这个问题一般有解决办法吗?
-
当然,因此我不是 Reactive 的忠实粉丝。它也没有完全席卷世界。替代方案是优秀的样板,只需使用同步计时器。
标签: c# .net com concurrency rcw