I have a gui application, that periodically shows the cpu load. The load is read by a StateReader class:
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();
}
}
The gui is updated using reactive extensions:
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();
Now when I exit my application, I get an error saying
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.
This error doesn't appear if I don't read the cpu load, but just supply some random value, so the error is somehow connected to reading the load. Also if I put a breakpoint after Application.Run(gui)
and wait there for a bit, the error doesn't seem to come as often.
From this and from my googling 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, and when I exit my application, that thread doesn't have time to properly close the RCW, leading to my error. Is this correct, and how can I solve this problem?
I have edited my code to reflect the responses I have got, but I still get the same error. The code is updated on three points:
- StateReader is Disposable, disposes its ManagementObjectSearcher in the Dispose method and I call Dispose on the StateReader object after Application.Run in my main method
- In CPULoad I dispose of the ManagementCollection and each ManagementObject in it
- In my main method I dispose of the subscription object in an event handler on FormClosing
on the gui. This should ensure that no events are generated for the gui after it is closed.
The relevant parts of the code are now, in 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();
}
And in my main:
gui.FormClosing += (a1, a2) => sub.Dispose();
Application.Run(gui);
reader.Dispose();
Is there anything else I could do to avoid the error I get?