Многопоточность звонит в приложение Windows Forms?

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

Обзор моей программы - то, что я хочу сделать производительность, контролирующую applicaiton. То, что это влечет за собой, использует процесс и класс счетчика производительности в 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;
}
6
задан Esteban Küber 28 February 2010 в 23:09
поделиться

3 ответа

Прочтите статью Джона Скита о многопоточности, в частности страницу на многопоточных winforms . Это должно вас исправить.

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

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.

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

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

Возможно, вам покажется этот маршрут проще, но я его не пробовал.

7
ответ дан 17 December 2019 в 00:08
поделиться

Я думаю, ваша проблема в том, что эта строка:

crntreadingslbl.Text = cpuReading.ToString();

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

С учетом всего сказанного, почему бы не использовать perfmon? Он создан специально.

0
ответ дан 17 December 2019 в 00:08
поделиться

Компонент 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   
}
0
ответ дан 17 December 2019 в 00:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: