CPU負荷を定期的に表示するGUIアプリケーションがあります。負荷は 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)
待つと、エラーはそれほど頻繁には発生しないようです。
これと私のグーグル検索から、管理名前空間でクラスを使用すると、Runtime Callable Wrapper でラップされた COM オブジェクトを参照するバックグラウンド スレッドが作成され、アプリケーションを終了すると、そのスレッドは適切に閉じる時間がないと思います。 RCW、私のエラーにつながります。これは正しいですか、どうすればこの問題を解決できますか?
受け取った応答を反映するようにコードを編集しましたが、それでも同じエラーが発生します。コードは次の 3 点で更新されます。
- StateReader は Disposable であり、その ManagementObjectSearcher を Dispose メソッドで破棄し、メイン メソッドで Application.Run の後に StateReader オブジェクトで Dispose を呼び出します。
- CPULoad では、ManagementCollection とその中の各 ManagementObject を破棄します
- メイン メソッドでは、GUI の 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();
}
そして私のメインでは:
gui.FormClosing += (a1, a2) => sub.Dispose();
Application.Run(gui);
reader.Dispose();
エラーを回避するために他にできることはありますか?