2012-03-07 5 views
4

주기적으로 CPU 부하를 표시하는 GUI 응용 프로그램이 있습니다. 부하는 StateReader 클래스에 의해 읽기 :RCW 정리에서 레이스를 피하는 방법

GUI는 반응 확장 갱신된다
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(); 
    } 
} 

: 내 응용 프로그램을 종료 할 때, 나는

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. 

말하는 오류가 이제

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(); 

을 이 오류는 CPU 부하를 읽지 않고 일부 임의의 값만 제공하면 나타나지 않으므로 오류가 어떻게 든로드 읽기에 연결됩니다. 또한 Application.Run(gui) 뒤에 중단 점을 넣고 약간 기다리면 오류가 자주 발생하지 않는 것 같습니다.

이것과 내 인터넷 검색에서 나는 관리 네임 스페이스의 클래스를 사용하여 런타임 호출 가능 래퍼에 래핑 된 COM 객체를 참조하는 백그라운드 스레드를 만들고 응용 프로그램을 종료하면 해당 스레드에 시간이 없다고 생각합니다. RCW를 제대로 닫아서 실수로 이어집니다. 이게 맞습니까? 어떻게이 문제를 해결할 수 있습니까?


내가 가진 응답을 반영하도록 코드를 편집했지만 여전히 동일한 오류가 발생합니다. 이 코드는 세 가지 점에 업데이트됩니다

  • StateReader은 일회용이며, Dispose 메서드 에서의 ManagementObjectSearcher을 처분하고 난 처분 CPULoad에서
  • 내 주요 방법에 Application.Run 후 StateReader 개체에서 Dispose를 호출해야 ManagementCollection 및 그 안에있는 각 ManagementObject
  • 내 주요 메소드에서 gui의 FormClosing
    의 이벤트 처리기에 구독 객체를 삭제합니다. 이렇게하면 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

@ 한자 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에서는 거의 사용할 수 없습니다.