简体   繁体   中英

How can i make a thread “report back” to main thread?

Im making a app that monitors stuff on my computer, and i want to make it somewhat more difficult then just implementing a while loop.

So my question is how do i do it if i would like to fetch cpu load in a seperate thread, that updates a static variable in class

namespace threads
{
    class Program
    {
        static int cpuload = 0;

        static void Main(string[] args)
        {
            while (true)
            {
                Thread th = new Thread(new ThreadStart(CheckCPULoad));
                th.Start();

                Thread.Sleep(1000); // sleep the main thread

                th.Abort();

                Console.WriteLine("load: {0}%", cpuload);
            }
        }

        static void CheckCPULoad()
        {
            // things are updated every 3 secs, dummy data
            Thread.Sleep(3000);

            Random rnd = new Random();
            cpuload++;// = rnd.Next(0, 100); // dummy data
        }
    }
}

As it is "load: 0%" is printed every time. what do i need to fix to make it show

load: 0% 
load: 0% 
load: 0% 

?

thanks

In order to "report back" to the main thread, the main thread has to be "listening". Which means, still running in a while loop and checking some kind of a queue for new items that represent the reports.

What you basically need is a queue where the worker thread will put its reports, and the main thread will periodically check this queue for reports from the worker.

You have two main approaches:

  1. A blocking queue. Means that when there are no items the caller thread blocks until items arrive. This is good if the main thread has nothing to do except wait for items in the queue.
  2. A non-blocking queue. Means that it returns immediately to the caller regardless of the items count. This is good if you want your main thread to be busy doing stuff and sometimes checking the queue for reports.

If your application is a UI application you automatically get the first approach, as this is how the UI works. To add "an item" you can use Control.BeginInvoke (in winforms) or Dispatcher.BeginInvoke (in wpf).

The code you're using there starts the CheckCPULoad thread, waits 1 second and then aborts it. However, the first thing the CheckCPULoad thread does is to sleep for 3 seconds. So you never actually reach the cpuload++ instruction. I suspect this would be closer to what you intended:

namespace threads
{
    class Program
    {
        static int cpuload = 0;

        static void Main(string[] args)
        {
            Thread th = new Thread(new ThreadStart(CheckCPULoad));
            th.Start();

            while (true)
            {
                Thread.Sleep(1000);
                Console.WriteLine("load: {0}%", cpuload);
            }

            th.Abort(); // Don't ever reach this line with while (true)
        }

        static void CheckCPULoad()
        {
            while (true)
            {
                Thread.Sleep(3000);
                cpuload++;
            }
        }
    }
}

Use a timer and events instead. This way you avoid your sleeping/busy waiting. Also consider using Interlocked.Increment as suggested if several threads can modify the static variable at the same time.

using System;
using System.Threading;
using System.Timers;
using Timer = System.Timers.Timer;

namespace CpuLoad
{
    internal class Program
    {
        private static int cpuload;
        private static readonly AutoResetEvent autoEvent = new AutoResetEvent(false);

        private static void Main(string[] args)
        {
            var timer = new Timer(3000);
            timer.Elapsed += CheckCPULoad;
            timer.Start();

            while (true)
            {
                autoEvent.WaitOne();
                autoEvent.Reset();
                Console.WriteLine(cpuload);
            }
        }

        private static void CheckCPULoad(object sender, ElapsedEventArgs e)
        {
            cpuload++;
            autoEvent.Set();
        }
    }
}

If i get you right, this should solve your purpose. Notice the while loop inside the CheckCPULoad() method.

class Program 
        {
            static int cpuload = 0;

    static void Main(string[] args)
    {
        Thread th = new Thread(new ThreadStart(CheckCPULoad));
        th.Start();

        while (true)
        {
            Thread.Sleep(1000);
            Console.WriteLine("load: {0}%", cpuload);
        }
        th.Abort(); // Don't ever reach this line with while (true)        
    }

    static void CheckCPULoad()
    {
        while (true)
        {
            Thread.Sleep(3000);
            cpuload++;
        }
    }


}

Instead of cpuload++ try using

Interlocked.Increment(ref cpuload);

Check - http://msdn.microsoft.com/en-us/library/dd78zt0c.aspx

The thread sleeps for 3 secs. You abort it after 1. Go figure :)

With callback you can do that

here is an exsample:

public class Example2
{
    // Declaration - Take 1 parameter, return nothing
    public delegate void LogHandler(string message);

    // Instantiation - Create a function which takes delegate as one parameter
    // Verify if it is null before you use it
    public void Process(LogHandler logHandler)
    {
        if (logHandler != null)
        {
            logHandler("Process() begin");
        }

        if (logHandler != null)
        {
            logHandler("Process() end");
        }
    }
}

public class Example2DelegateConsumer
{
    // Create a method with the same signature as the delegate
    static void Logger(string s)
    {
        Console.WriteLine(s);
    }

    public static void Main(string[] args)
    {
        Example2 ex2 = new Example2();

        // Invocation in the client
        Example2.LogHandler myLogger = new Example2.LogHandler(Logger);
        ex2.Process(myLogger);
    }
}

In addition to my original (plagiarised) answer below, this sort of situation where you're observing a set of values over time is a great fit for Reactive Extensions for .NET (http://blogs.msdn.com/b/rxteam/). You can get the desired effect with Rx thus:

static void Main()
{
    var cpuLoadSequence = Observable.GenerateWithTime(
        0, // initial value
        i => true, // continue forever
        i => i + 1, // increment value
        i => i, // result = value
        i => TimeSpan.FromSeconds(3)); // delay 3 seconds

    using (cpuLoadSequence.Subscribe(x => Console.WriteLine("load: {0}%", x)))
    {
        Console.WriteLine("Press ENTER to stop.");
        Console.ReadLine();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM