c# - 如何避免 RCW 清理上的竞争

标签 c# .net com concurrency rcw

我有一个 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,导致我的错误。这是否正确,我该如何解决这个问题?


我已经编辑了我的代码以反射(reflect)我得到的响应,但我仍然遇到同样的错误。代码更新了三点:

  • StateReader 是 Disposable,在 Dispose 方法中配置其 ManagementObjectSearcher 我在主方法中的 Application.Run 之后调用 StateReader 对象上的 Dispose
  • 在 CPULoad 中,我处理了 ManagementCollection 和其中的每个 ManagementObject
  • 在我的主要方法中,我在 FormClosing 的事件处理程序中处理了订阅对象
    在图形用户界面上。这应确保在关闭后不会为 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();

还有什么我可以做的来避免我得到的错误吗?

最佳答案

我认为您需要使 StateReader 成为一次性的,并在退出您的应用程序之前将其处置掉。 StateReader 应该处理 searcher。但是,我认为真正的问题是您没有在 CPULoad 中处理 ManagementObject。如果 GC 在 CPULoad 之后运行,RCW 将被释放。但是,如果您在 GC 之前退出,那么这可能会触发您看到的异常。

I think that using the classes in the management namespace creates a background thread that references a COM object wrapped in a Runtime Callable Wrapper

Observable.Interval 创建一个后台线程,CPULoad 在该线程上执行。

关于c# - 如何避免 RCW 清理上的竞争,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9599538/

相关文章:

c# - Sharepoint 将项目添加到图片库中

c# - Rijndael加密/解密

python - 如何用Python制作持久的com服务器

c# - 自动化 Windows 防火墙

c# - xslt V1.0 - 具有递归循环的子模板返回空值

c# - __declspec(dllexport)::vector <std::string>

c# - 当文档建议我不能读取超过 254 个字符时,为什么我可以使用 Console.ReadLine 读取超过 254 个字符?

c# - NancyFx 测试 : The type or namespace Bootstrapper cannot be found

c# - CUDAfy.NET 给出 Win32Exception : The system cannot find the file specified

python - Web 服务器 MSAccess 错误 : You canceled the previous operation