Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何避免RCW清理上的竞争_C#_.net_Com_Concurrency_Rcw - Fatal编程技术网

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

C# 如何避免RCW清理上的竞争,c#,.net,com,concurrency,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 =

我有一个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();
    }
}
现在,当我退出我的应用程序时,我得到一个错误

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是一次性的,在Dispose方法中处置其ManagementObjectSearcher 我在Application.Run之后调用StateReader对象的Dispose
  • 在CPULoad中,我处理ManagementCollection和其中的每个ManagementObject
  • 在我的main方法中,我在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();
}

我还可以做些什么来避免出现错误?

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

我认为使用管理名称空间中的类会创建一个后台线程,该线程引用一个封装在运行时可调用包装器中的COM对象


可观察。Interval
创建一个后台线程,并在该线程上执行
CPULoad

后台线程运行时不要让应用程序退出
CPULoad
,以避免它

我能想到的最简单的解决方案是从一个新的前台线程获得CPU负载,然后加入线程

public UInt64 CPULoad()
{
    List<UInt64> list = new List<UInt64>();
    Thread thread = new Thread(() =>
    {
        ManagementObjectCollection results = searcher.Get();
        foreach (ManagementObject result in results)
        {
            list.Add((UInt64)result.Properties["PercentProcessorTime"].Value);
        }
    });
    thread.Start();
    thread.Join();
    return list.Max();
}
public UInt64 CPULoad()
{
列表=新列表();
线程线程=新线程(()=>
{
ManagementObjectCollection结果=searcher.Get();
foreach(管理对象结果)
{
list.Add((UInt64)result.Properties[“PercentProcessorTime”].Value);
}
});
thread.Start();
thread.Join();
返回list.Max();
}
与缓慢的WMI调用相比,每次启动新线程的开销可以忽略不计


注释中提到的同步计时器实现了几乎相同的行为,但它阻止了UI线程,并且很难与慢速WMI一起使用。

您的诊断是正确的。这不是唯一的问题,.ObserveOn(gui)调用也非常麻烦。在允许表单关闭之前,必须确保不能再生成通知。这就是允许线程猖獗运行的危险。@Hans Passant:我已经编辑了代码,以便在FormClosing上处理订阅。你会说这解决了你提到的问题吗?我在表单上没有其他事件,除了用户与表单交互时产生的事件,如按钮单击等。可能不会,如果在调用Dispose()时计划了一个TP线程,但尚未开始执行,那么这就是线程竞赛。“我对反应式管道不太了解。”“汉帕桑:我没想过。但是仔细想想,这不适用于从另一个线程调用表单上的方法的任何方案吗?总的来说,这个问题有什么解决办法吗?当然,因为这个原因,我不太喜欢反应式。而且它也没有完全席卷世界。另一种选择是优秀的样板,只需使用同步计时器。谢谢,我忘记了Observable.Interval中的背景线程。我已经编辑了我的代码以反映您的建议,但是我仍然得到了错误。
gui.FormClosing += (a1, a2) => sub.Dispose();

Application.Run(gui);
reader.Dispose();
public UInt64 CPULoad()
{
    List<UInt64> list = new List<UInt64>();
    Thread thread = new Thread(() =>
    {
        ManagementObjectCollection results = searcher.Get();
        foreach (ManagementObject result in results)
        {
            list.Add((UInt64)result.Properties["PercentProcessorTime"].Value);
        }
    });
    thread.Start();
    thread.Join();
    return list.Max();
}