私は定期的にCPU負荷を表示するGUIアプリケーションを持っています。負荷がStateReaderクラスによって読み込まれます。RCWクリーンアップでレースを避ける方法
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を正しく閉じると、私のエラーにつながります。これは正しいですか?この問題をどうやって解決できますか?
回答を反映するためにコードを編集しましたが、同じエラーが表示されます。コードは三点に更新されます。
- StateReaderは使い捨てで、Disposeメソッド でのManagementObjectSearcherを配置し、私は私が処分すなわち、cpuloadで
- 私の主な方法でApplication.Run後StateReaderオブジェクト上のDisposeを呼び出します管理コレクションとその中の各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();
あります?
あなたの診断は正しいです。それは唯一の問題ではなく、。ObserveOn(GUI)呼び出しも非常に面倒です。フォームを閉じることを許可する前に、通知が生成されないようにする必要があります。これは、スレッドが横行するのを許す危険性があります。 –
@Hans Passant:FormClosingでサブスクリプションを処分するコードを編集しました。これは、あなたが言及した問題を解決すると言いますか?私はフォーム上で、ボタンのクリックなどのような、ユーザーとの対話から生じるイベント以外のイベントはありません。 – Boris
おそらく、Dispose()を呼び出してもまだ実行を開始していないときにTPスレッドがスケジュールされていれば、スレッドレースです。私は十分に反応的な配管工事を知らない。 –