2010-02-28 2 views
6

가끔씩, 안전하지 않은 방식으로 스레드를 호출했다는 예외가 발생하기 때문에 C# 응용 프로그램을 멀티 스레드로 만들려고합니다. 나는 프로그램에서 멀티 스레딩을 한 번도 해 본 적이 없으므로이 문제에 대해 무지한 생각이 든다면 나에게 곰곰이 생각해보십시오.Windows Forms 응용 프로그램에서 멀티 스레딩 호출?

내 프로그램의 개요는 성능 모니터링 응용 프로그램을 만들고 싶다는 것입니다. 이것이 수반하는 일은 C#의 프로세스 및 성능 카운터 클래스를 사용하여 응용 프로그램의 프로세서 시간을 시작 및 모니터링하고 해당 번호를 다시 UI로 보내는 것입니다. 그러나 실제로 성능 카운터의 nextValue 메서드 (타이머 덕분에 매 초마다 수행하도록 설정 됨)를 호출하는 메서드에서 스레드를 안전하지 않은 방식으로 호출하는 것에 대해 설명한 앞서 언급 한 예외가 발생합니다.

나는 당신의 열정을위한 몇 가지 코드를 첨부했습니다. 나는 이것이 시간이 많이 걸리는 문제라는 것을 알고 있습니다. 그래서 누군가 새로운 스레드를 만들 수있는 곳과 안전한 방법으로 호출하는 방법에 대해 도움을 줄 수 있다면 정말로 감사 할 것입니다. MSDN에서 무슨 일이 있었는지 살펴 보았습니다. 그러나 다소 혼란 스럽습니다.

private void runBtn_Click(object sender, EventArgs e) 
{ 
    // this is called when the user tells the program to launch the desired program and 
    // monitor it's CPU usage. 

    // sets up the process and performance counter 
    m.runAndMonitorApplication(); 

    // Create a new timer that runs every second, and gets CPU readings. 
    crntTimer = new System.Timers.Timer(); 
    crntTimer.Interval = 1000; 
    crntTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent); 
    crntTimer.Enabled = true; 
} 

private void OnTimedEvent(object source, ElapsedEventArgs e) 
{ 
    // get the current processor time reading 
    float cpuReading = m.getCPUValue(); 

    // update the current cpu label 
    crntreadingslbl.Text = cpuReading.ToString(); // 

} 
// runs the application 
public void runAndMonitorApplication() 
{ 
    p = new Process(); 
    p.StartInfo.UseShellExecute = true; 
    p.StartInfo.CreateNoWindow = true; 
    p.StartInfo.FileName = fileName; 
    p.Start(); 

    pc = new System.Diagnostics.PerformanceCounter("Process", 
       "% Processor Time", 
       p.ProcessName, 
       true); 
} 

// This returns the current percentage of CPU utilization for the process 
public float getCPUValue() 
{ 
    float usage = pc.NextValue(); 

    return usage; 
} 

답변

7

체크 아웃 멀티 스레딩에 존 소총의 기사, multi-threading winforms에 특히 페이지를. 너를 바로 잡을거야.

기본적으로 호출이 필요한지 확인하고 필요한 경우 호출을 수행해야합니다. 기사를 읽은 후에는 다음과 같이 블록으로 UI 업데이트되는 코드를 리팩토링 할 수 있어야한다 : 당신이 타이머를 사용하고 있기 때문에 코드에서

private void OnTimedEvent(object source, ElapsedEventArgs e) 
{ 
    // get the current processor time reading 
    float cpuReading = m.getCPUValue(); 

    if (InvokeRequired) 
    { 
     // We're not in the UI thread, so we need to call BeginInvoke 
     BeginInvoke(new Action(() => crntreadingslbl.Text = cpuReading.ToString())); 
     return; 
    } 
    // Must be on the UI thread if we've got this far 
    crntreadingslbl.Text = cpuReading.ToString(); 
} 

가, 인보이 필요합니다. System.Timers.Timer에 대한 설명서에 따라 :

Elapsed 이벤트는 ThreadPool 스레드에서 발생합니다.

이것은 Timer 's delegate로 설정 한 OnTimedEvent() 메서드가 다음 사용 가능한 ThreadPool 스레드에서 실행된다는 것을 의미합니다.이 스레드는 사용자의 UI 스레드가 아닙니다. 받는 타이머를 포함 폼이나 컨트롤 당신은 폼이나 컨트롤로, 사용자 인터페이스 요소와 타이머를 사용하는 경우,

지정 : 문서는이 문제를 해결하기 위해 다른 방법을 제안 SynchronizingObject 속성이므로 이벤트는 사용자 인터페이스 스레드로 마샬링됩니다.

이 경로를 쉽게 찾을 수 있지만 시도하지는 않았습니다.

+0

이것과 배경 작업자 댓글이 꽤 유용한 것, 좋아; 하지만 그것을 이해, 프로세스 자체가 UI 스레드에서 실행되고 있지만 해당 프로세스에서 데이터를 수집하고 업데이트하려면 별도의 스레드를 만들어야합니까? 일반적으로 별도의 스레드를 만들 위치를 어떻게 알 수 있습니까? – Waffles

+0

타이머는 "꺼짐"일 때 사용 가능한 첫 번째 ThreadPool 스레드에서 요청 된 ElapsedEventHandler 대리자를 실행합니다. 따라서 타이머에 요청한 것은 UI 스레드가 아닌 별도의 스레드에서 발생합니다. 배경 작업자를 추가하면 방정식에 또 다른 스레드가 추가됩니다. –

0

귀하의 문제는, 내가 생각이다이 라인이 :

crntreadingslbl.Text = cpuReading.ToString(); 

는 UI 스레드 외부에서 실행됩니다. UI 스레드 외부의 UI 요소는 업데이트 할 수 없습니다. UI 스레드에서 새 메서드를 호출하려면 창에서 Invoke를 호출해야합니다.

모두 perfmon을 사용하지 않는 이유는 무엇입니까? 그것은 목적을 위해 세워졌습니다.

0

BackGroundWorker 구성 요소가 도움이 될 수 있습니다. 도구 상자에서 사용할 수 있으므로 양식으로 끌 수 있습니다.

이 구성 요소는 UI 스레드와 다른 스레드에서 작업을 실행하기 위해 일련의 이벤트를 제공합니다. 스레드를 만드는 것에 대해 걱정할 필요가 없습니다.

배경에서 실행되는 코드와 UI 컨트롤 간의 모든 상호 작용은 이벤트 처리기를 통해 수행되어야합니다.

시나리오의 경우 특정 간격으로 백그라운드 작업자를 시작하도록 타이머를 설정할 수 있습니다.

private void OnTimedEvent(object source, ElapsedEventArgs e) 
{ 
    backgroundWorker.RunWorkerAsync(); 
} 

그런 다음 실제로 UI를 데이터를 수집하고 업데이트 할 수있는 적절한 이벤트 핸들러를 구현

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    // Collect performance data and update the UI 
}