Я пытаюсь подать свою заявку 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;
}
Прочтите статью Джона Скита о многопоточности, в частности страницу на многопоточных 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 , так что событие маршалируется в поток интерфейса пользователя .
Возможно, вам покажется этот маршрут проще, но я его не пробовал.
Я думаю, ваша проблема в том, что эта строка:
crntreadingslbl.Text = cpuReading.ToString();
выполняется вне потока пользовательского интерфейса. Вы не можете обновить элемент пользовательского интерфейса вне потока пользовательского интерфейса. Вам нужно вызвать Invoke в окне, чтобы вызвать новый метод в потоке пользовательского интерфейса.
С учетом всего сказанного, почему бы не использовать perfmon? Он создан специально.
Компонент 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
}