2010-02-28 3 views
6

Я пытаюсь сделать мое приложение C# многопоточным, потому что иногда я получаю исключение, которое говорит, что я сделал вызов потока небезопасным образом. Я никогда не делал многопоточности раньше в программе, так что несите меня, если я буду казаться немного невежественным в этой проблеме.Многопоточные вызовы в приложении Windows Forms?

Обзор моей программы заключается в том, что я хочу сделать приложение для мониторинга производительности. Это подразумевает использование класса производительности и производительности в C# для запуска и мониторинга времени процессора приложения и отправки его обратно в пользовательский интерфейс. Однако в методе, который на самом деле вызывает метод 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(); 
} 

В коде Invoke будет необходимо, потому что вы используете таймер. В соответствии с документацией для System.Timers.Timer:

Истекшее событие поднимается в потоке ThreadPool.

Это означает, что метод OnTimedEvent(), который вы задали как делегат таймера, будет выполняться в следующем доступном потоке ThreadPool, который определенно не будет вашим потоком пользовательского интерфейса. В документации также предлагает альтернативный способ решения этой проблемы:

Если вы используете таймер с элементом интерфейса пользователя , такие как форма или контроля, назначить форму или элемент управления , который содержит таймер на SynchronizingObject property, так что событие отправлено на адрес пользователя интерфейс.

Этот маршрут может быть проще, но я не пробовал.

+0

Хорошо, это и фон рабочий комментарий, кажется, очень полезно; но, как я понимаю, сам процесс работает в потоке пользовательского интерфейса, но я должен создать отдельный поток для сбора и обновления данных по этому процессу? В общем, как я смогу сказать, где сделать отдельный поток? – Waffles

+0

Таймер выполнит запрос делегата ElapsedEventHandler в первом доступном потоке ThreadPool, когда таймер «отключится». Поэтому все, что вы просили таймер, будет происходить в отдельном потоке, а не в потоке пользовательского интерфейса. Добавление фонового работника будет просто ввести еще один поток в уравнение. –

0

Ваша проблема, я думаю, что эта линия:

crntreadingslbl.Text = cpuReading.ToString(); 

выполняется за пределами нити UI. Вы не можете обновить элемент пользовательского интерфейса вне потока пользовательского интерфейса. Вам нужно вызвать Invoke в окне, чтобы вызвать новый метод в потоке пользовательского интерфейса.

Все, что сказал, почему бы не использовать перфтонов? Он построен специально.

0

Компонент BackGroundWorker может вам помочь. Он доступен на панели инструментов, чтобы вы могли перетащить его в свою форму.

Этот компонент предоставляет набор событий для выполнения задач в потоке, отличном от потока пользовательского интерфейса. Вам не нужно беспокоиться о создании потока.

Все взаимодействие между кодом, выполняющимся на фоне и элементами управления пользовательского интерфейса, должно выполняться с помощью обработчиков событий.

Для вашего сценария вы можете настроить таймер для запуска фонового работника с определенным интервалом.

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

Затем выполнить соответствующие обработчики событий на самом деле сбора данных и обновления пользовательского интерфейса

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

 Смежные вопросы

  • Нет связанных вопросов^_^