0

Edit: I have removed .Sleep(5000) from the Run() method and replaced that with a double loops that simulate complex calculations.

I would like to understand how to control a Thread with ManualResetEvent? I wrote that simple piece of code which is supposed to start, stop and resume a foreground Thread but it does not work as expected.

using System;
using System.Threading;

namespace ThreadHandler
{
    public class Engine
    {
        public static ManualResetEvent ThreadHandle;
        public static void Run()
        {
            Random rng = new Random();
            ThreadHandle = new ManualResetEvent(true);
            while (true)
            {
                ThreadHandle.WaitOne();
                for (int i = Int32.MaxValue; i > 0; i--)
                {
                    for (int j = 0; j < rng.Next(); j++)
                    {
                        int res;
                        int div =    
Math.DivRem(Math.Abs(Convert.ToInt32(Math.Floor(Math.Log(rng.NextDouble()) / 
Math.Log(rng.NextDouble())))),                           
Math.Abs(Convert.ToInt32(Math.Floor(Math.Log(rng.NextDouble()) /Math.Log(rng.NextDouble())))) + 1, out res);
                        double calc = Math.Sin(Math.Sqrt(div)) * Math.Cos(Math.Sqrt(res));
                    }
                    if (i % 500 == 0)
                        Console.WriteLine("This engine is working hard!!!");
                }
            }
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            Thread engine = new Thread(Engine.Run);
            int cnt = 100;
            while (cnt-- > 0)
            {
                Console.WriteLine("\nThread status: " +  engine.ThreadState);
                Console.WriteLine("Enter command line (Start/Wait/Set/Status/Quit)");
                string cmd = Console.ReadLine();
                switch(cmd.ToUpper())
                {
                    case "START":
                        engine.Start();
                        break;
                    case "WAIT":
                        Engine.ThreadHandle.WaitOne(Timeout.Infinite);
                        break;
                    case "SET":
                        Engine.ThreadHandle.Set();
                        break;
                    case "STATUS":
                        Console.WriteLine("  >" + engine.ThreadState);
                        break;
                    case "QUIT":
                        cnt = 0;
                        break;
                    default:
                        Console.WriteLine("Unknown command");
                        break;
                }
            }
            engine.Abort();
        }
    }
}

When I run the program, the Thread is unstarted:

    Thread status: Unstarted
    Enter command line (Start/Wait/Set/Status/Quit)

After starting the Thread, things gets weird. The ThreadState immediately changes from Running to WaitSleepJoin. What is the reason for that? I thought the Thread would remain in its Running state until we call the method WaitOne().

Edit: The Thread starts and its ThreadState is logically Running

Thread status: Unstarted
Enter command line (Start/Wait/Set/Status/Quit)
start

Thread status: Running
Enter command line (Start/Wait/Set/Status/Quit)
This engine is working hard!!!
status
  >Running

Then when I type in Wait, the Thread stays in WaitSleepJoin, but the Engine keeps on running.

Edit: The problem I now have is how can I stop it? When I call

Engine.ThreadHandle.WaitOne(Timeout.Infinite);

The Thread keeps running:

Thread status: Unstarted
Enter command line (Start/Wait/Set/Status/Quit)
start

Thread status: Running
Enter command line (Start/Wait/Set/Status/Quit)
This engine is working hard!!!
status
  >Running

Thread status: Running
Enter command line (Start/Wait/Set/Status/Quit)
This engine is working hard!!!
wait

Thread status: Running

Finally when I type in Set, the Thread never go back to Running state.

My questions remains are:

  1. How can I interrupt the Thread Engine bay calling the method WaitOne()?
  2. How can I resume it after?
Hydraxize
  • 161
  • 10
  • The engine thread is in `WaitSleepJoin` because of the `Sleep` command you have in it. Your engine thread needs to call Wait one - right now it's your "main" console thread that does that... – fredrik Aug 08 '18 at 08:58
  • Thank you fredrik, I've added ThreadHandle.WaitOne(); in my `Engine` loop but the engine thread keeps on changing to `WaitSleepJoin` after it starts – Hydraxize Aug 08 '18 at 09:08
  • It will continue to do that as it's sleeping... first in the `.Sleep()` and then in the `WaitOne()`. – fredrik Aug 08 '18 at 09:24
  • I removed the `.Sleep(5000)`, it is now running all the time but still fail to pause it. – Hydraxize Aug 10 '18 at 08:29
  • The code in the Run() method is very, very slow. Once you got it running, it is going to be running for quite a long time and nothing seems to have any affect. You need to adapt it to make it compatible with your patience, cut down on the loops so it never takes more than 50 milliseconds. – Hans Passant Aug 10 '18 at 08:42
  • It's a `ManualResetEvent` and nothing is ever calling `Reset` on it. How do you expect it to ever become unset? – Damien_The_Unbeliever Aug 10 '18 at 09:42

1 Answers1

0

Your WaitOne() will never work as it's in the main loop while the code executing is after (the two for loops).
You need to move it down:

while (true)
{
    for (int i = Int32.MaxValue; i > 0; i--)
    {
        for (int j = 0; j < rng.Next(); j++)
        {
            ThreadHandle.WaitOne();
            // ...
        }

        if (i % 500 == 0)
            Console.WriteLine("This engine is working hard!!!");
    }
}

Result

Thread status: WaitSleepJoin
Enter command line (Start/Wait/Set/Status/Quit)

Note
I believe that you're miss-using ManualResetEvent as the WaitOne method needs to be called by who's gonna wait (the Thread in your case) but you're calling it twice. You should probably use a boolean to signal to the thread that it should start the WaitOne rather.

Haytam
  • 4,643
  • 2
  • 20
  • 43
  • Ok, so now I'm using a `static` boolean to know when to call `WaitOne()`. I was trying to avoid that solution because I need to lock the variable everytime I am changing it. The `Thread` turns to `WaitSleepJoin` for a little while, but then starts again on his own. Even when I use `WaitOne(Timeout.Infinite)`. Is there a way to make it Wait until I call `.Set()`? – Hydraxize Aug 10 '18 at 09:27