У меня есть приложение с графическим интерфейсом, которое периодически показывает загрузку процессора.Загрузка читается классом 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();
}
}
Графический интерфейс обновляется с использованием реактивных расширений:
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.
Эта ошибка не появляется, если я не читаю загрузку процессора, а просто предоставляю какое-то случайное значение, поэтому ошибка каким-то образом связана с чтением загрузки.Также, если я поставлю точку останова после Application.Run(gui)
и подожду там немного, ошибка, похоже, будет возникать не так часто.
Исходя из этого и из моего поиска в Google, я думаю, что использование классов в пространстве имен управления создает фоновый поток, который ссылается на COM-объект, обернутый в Runtime Callable Wrapper, и когда я выхожу из приложения, этот поток неуспеть правильно закрыть RCW, что приведет к моей ошибке.Это правильно, и как я могу решить эту проблему?
Я отредактировал свой код, чтобы отразить полученные ответы, но я все еще получаю ту же ошибку.Код обновляется по трем точкам:
- StateReader является Disposable, удаляет его ManagementObjectSearcher в методе Dispose, и я вызываю Dispose для объекта StateReader после Application.Run в моем основном методе
- В CPULoad я располагаю ManagementCollection и каждый ManagementObject в нем
- . В моем основном методе я располагаю объект подписки в обработчике событий FormClosing
в графическом интерфейсе.Это должно гарантировать, что никакие события не генерируются для графического интерфейса после его закрытия.
Соответствующие части кода теперь находятся в 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();
Могу ли я еще что-нибудь сделать, чтобы избежать получаемой ошибки?