Multi-threading calls in Windows Forms application?

13,466

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.

Share:
13,466
Waffles
Author by

Waffles

Updated on June 17, 2022

Comments

  • Waffles
    Waffles almost 2 years

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