Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multi-threading calls in Windows Forms application?

I'm trying to make my C# application multi threaded because sometimes, I get an exception that says I have made a call to a thread in an unsafe manner. I've never done any multi-threading before in a program, so bear with me if I sound kinda ignorant on the issue.

The overview of my program is that I want to make a performance monitoring applicaiton. What this entails is using the process and performance counter class in C# to launch and monitor an application's processor time, and sending that number back to the UI. However, in the method that actually calls the performance counter's nextValue method (which is set to perform every second thanks to a timer), I would sometimes get the aforementioned exception that would talk about calling a thread in an unsafe manner.

I've attached some of the code for your perusal. I know this is kind of a time consuming question, so I'd be really grateful if anyone could offer me any help as to where to make a new thread and how to call it in a safe way. I tried looking at what was up on MSDN, but that just kinda confused me.

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;
}
like image 400
Waffles Avatar asked Feb 28 '10 22:02

Waffles


People also ask

Is Winforms multithreaded?

A number of tools are available for multithreading your Windows Forms controls, including the System. Threading namespace, the Control. BeginInvoke method, and the BackgroundWorker component. The BackgroundWorker component replaces and adds functionality to the System.

What are examples of multi threaded applications?

Another example of a multithreaded program that we are all familiar with is a word processor. While you are typing, multiple threads are used to display your document, asynchronously check the spelling and grammar of your document, generate a PDF version of the document.

Is Winforms single threaded?

The answer is yes, although it's infrequently done. You can have a distinct thread for each form (although each control would only have one associated UI thread).

Why would we want to use multiple threads in our application?

Multi-threading allows you to make the best use out of your existing hardware resources and also allows simple resource sharing. There are, of course, disadvantages in using multi-threaded applications. Each thread must be aware of the resources that it might be sharing with other threads in the same process.


1 Answers

Check out Jon Skeet's article on multi-threading, particularly the page on multi-threading winforms. It should fix you right up.

Basically you need to check to see if an invoke is required, and then perform the invoke if needed. After reading the article you should be able to refactor your UI-updating code into blocks that look like this:

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

In your code, an invoke will be required because you are using a Timer. According to the documentation for System.Timers.Timer:

The Elapsed event is raised on a ThreadPool thread.

This means that the OnTimedEvent() method that you set as the Timer's delegate will execute on the next available ThreadPool thread, which will definitely not be your UI thread. The documentation also suggests an alternate way to solve this problem:

If you use the Timer with a user interface element, such as a form or control, assign the form or control that contains the Timer to the SynchronizingObject property, so that the event is marshaled to the user interface thread.

You may find this route easier, but I haven't tried it.

like image 64
Jim Counts Avatar answered Sep 30 '22 19:09

Jim Counts