2012-03-07 24 views
4

私は定期的に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(); 

あります?

+1

あなたの診断は正しいです。それは唯一の問題ではなく、。ObserveOn(GUI)呼び出しも非常に面倒です。フォームを閉じることを許可する前に、通知が生成されないようにする必要があります。これは、スレッドが横行するのを許す危険性があります。 –

+0

@Hans Passant:FormClosingでサブスクリプションを処分するコードを編集しました。これは、あなたが言及した問題を解決すると言いますか?私はフォーム上で、ボタンのクリックなどのような、ユーザーとの対話から生じるイベント以外のイベントはありません。 – Boris

+1

おそらく、Dispose()を呼び出してもまだ実行を開始していないときにTPスレッドがスケジュールされていれば、スレッドレースです。私は十分に反応的な配管工事を知らない。 –

答えて

1

StateReaderを使い捨てにして、アプリケーションを終了する前に処分する必要があると思います。 StateReadersearcherを処分する必要があります。しかし、私は本当の問題は、あなたがCPULoadManagementObjectを処分していないということです。 GCがCPULoadの後に実行されると、RCWは解放されます。しかし、GCの前に終了すると、例外が発生する可能性があります。

は私が管理名前空間のクラスを使用すると、ランタイム呼び出し可能ラッパー

Observable.Intervalに包まれたCOMオブジェクトを参照するバックグラウンドスレッドを作成し、CPULoadは、そのスレッド上で実行されるバックグラウンドスレッドを作成すると思います。

+1

ありがとう、Observable.Intervalのバックグラウンドスレッドを忘れました。あなたの提案を反映するためにコードを編集しましたが、まだエラーが発生します。 – Boris

0

バックグラウンドスレッドが実行されている間にアプリケーションを終了させないでください。CPULoadを避けてください。

私が考えることができる最も簡単な解決策は、新しいフォアグラウンドスレッドからCPU負荷を得て、次にスレッドに参加させることです。

public UInt64 CPULoad() 
{ 
    List<UInt64> list = new List<UInt64>(); 
    Thread thread = new Thread(() => 
    { 
     ManagementObjectCollection results = searcher.Get(); 
     foreach (ManagementObject result in results) 
     { 
      list.Add((UInt64)result.Properties["PercentProcessorTime"].Value); 
     } 
    }); 
    thread.Start(); 
    thread.Join(); 
    return list.Max(); 
} 

毎回新しいスレッドを開始するオーバーヘッドは、低速のWMI呼び出しと比較して無視できます。

コメントに記載されている同期タイマーは、ほぼ同じ動作を実現しますが、UIスレッドをブロックし、低速WMIではほとんど使用できません。

関連する問題