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:
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?
I think you need to make StateReader
disposable and dispose it before exiting your application. StateReader
should dispose searcher
. However, I think the real problem is that your are not disposing ManagementObject
in CPULoad
. If GC runs after CPULoad
the RCW's will be freed. However, if you exit before GC then this might be triggering the exception you see.
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
creates a background thread and CPULoad
executes on that thread.