1

I´m currently figuring out threads and how to work with them. At the same time Im working on my understanding on Events/Global Events (just for context).

I defined a thread inside a object with the function the thread will use on thread.Start().

internal class Name
    {
        private Thread testthread;

        private EventWaitHandle globalEvent;

        private Eventstest evente = new Eventstest(); //Just to add some methods

        public Name(Thread testthread, EventWaitHandle globalEvent)
        {
            this.testthread = testthread;
            this.globalEvent = globalEvent;
        }

        
        public void Execute()
        {
            bool terminate = false;
            bool eventset = false;
            bool rdy = false;

            while (!terminate)
            {
                if (evente.CheckSysEvent(globalEvent)) 
                {
                    eventset = true; //This is just to check with debugger if the event was raised elsewhere
                }
                Thread.Sleep(100);
            }
        }
    }

So now, like in this example in a Windows Forms App, Im trying to set an instance of this class while setting the instance of the thread at the same time (with the work method the should run later on). Im struggling with this part here.

private void btn_runThread_Click(object sender, EventArgs e)
        {
            threadClass = new Name(new Thread(ProblemHere), globalEvent);
            threadClass.Execute();            
        }

This is a button which starts the thread with the work its supposed to do. The variable threadClass is just the initialization in the forms1.cs:

Name threadClass;

I know that it wants a delegate to pass the method which the thread should use on start. I tried pretty much anything I found and cant make it work. I cant just pass the method, that doesnt work. And the stuff I found in the c# documentation is pretty much just passing the method, as far as I understood it. Which is propably wrong. And I just noticed, how am I able to actually call on that property/thread.start if its only created on runtime?

Rakku
  • 55
  • 4
  • You're assigning a thread instance to `testthread` and then run `Thread.Sleep(100)` on the main `Name` object itself. It doesn't make much of a sense as you'll probably `Sleep` the thread where the `Name` object was created from - possibly the main thread – Eric Jan 08 '21 at 14:37
  • You can try either inherit from Thread and call Thread.Sleep on `testthread` and see if that helps – Eric Jan 08 '21 at 14:38
  • _"This is a button which starts the thread with the work its supposed to do"_ - no it doesn't. You create a Thread, that _would_ execute `ProblemHere` if it was started, which it isn't. Then you go on to block the GUI thread. – Fildor Jan 08 '21 at 14:42
  • This looks like what you're trying to do. Create a new thread to call methods on another object. https://learn.microsoft.com/en-us/dotnet/standard/threading/creating-threads-and-passing-data-at-start-time – Aaron Glover Jan 08 '21 at 14:43
  • I am really not sure what you are trying to do here, but if you want an event-driven system, you shouldn't be polling for states but register for events to happen. You maybe need to set threads aside for now and concentrate on how to deal with events, first. – Fildor Jan 08 '21 at 14:45
  • @Fildor, you are absolutely right, but this is what I was tasked with, to actually get the hang of events. I do have already a functionality which lets me register for the event instead of polling it. But I wanted to try it this way, since it doesnt really matter if I poll or not in this example. Background is, to have a thread open, which constantly listens for an event to happen. – Rakku Jan 08 '21 at 15:01
  • _"to actually get the hang of events"_ what do you mean by that? – Fildor Jan 08 '21 at 15:03
  • To understand events, basicly. Im already fooling around with global registered events, and that part works and I somewhat understand the basic concept behind events. I just have some major difficulties to see the different context between things. – Rakku Jan 08 '21 at 15:11
  • And I just noticed, that @Rixment is absolutely right. I froze the mainthread instead of my thread instance. Unfortunaly I cant inerhit from thread, since its a sealed class. – Rakku Jan 08 '21 at 15:14
  • Yes, well. It's hard to give advice when we don't understand what it is exactly that you don't understand. If that makes sense. With the comments given, do you understand why the code in the question does not make sense? Why it is blocking the GUI thread and why `testthread` is not started? – Fildor Jan 08 '21 at 15:14
  • @Fildor I understand what you mean. I noticed that I was sleeping the main thread, since I was not calling sleep on any particular instance, just the main GUI thread. But unfortunaly I still dont understand how Im supposed to call a thread as a class property correctly and passing work to it to do. – Rakku Jan 08 '21 at 15:16
  • I guess you are mixing up some concepts. The ctor of `Thread` takes a delegate. That delegate determines, what will be executed _on_ that thread. Right now, you are making yourself a hard time by injecting the thread into the ctor of `Name`. Maybe start by creating the thread _in_ the ctor of `Name`, giving it the `Execute` method to execute and start it. Don't expect this to solve all your problems, but you can go on from there. – Fildor Jan 08 '21 at 15:20
  • By the way: I guess, if you only want to create a monitor for that "globalevent", you may come to a solution faster, if you have a look into [System.Windows.Forms.Timer](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.timer?view=net-5.0) – Fildor Jan 08 '21 at 15:27
  • Check the following answers at stackoverflow (especially the second one) https://stackoverflow.com/questions/1195896/threadstart-with-parameters – Eric Jan 08 '21 at 15:27
  • Thanks alot guys! Gotta research up a bit and Im done with work for today (head is spinning from reading for hours). Im going to look into the things you posted and will comment here again next week! – Rakku Jan 08 '21 at 15:39

2 Answers2

2

Not a full solution, but a bump to get you going:

What I would suggest is a little refactor like this

internal class Name
{
    private Thread testthread;

    private EventWaitHandle globalEvent;

    private Eventstest evente = new Eventstest(); //Just to add some methods

    public Name(EventWaitHandle globalEvent)
    {
        this.testthread = new Thread(Execute); // Creates a Thread, that is directed to execute `Execute`
        this.globalEvent = globalEvent;
        this.testthread.Start();  // Tells the framework to schedule the thread for execution.
    }
    
    private void Execute()
    {
        bool terminate = false;
        bool eventset = false;
        bool rdy = false;

        while (!terminate)
        {
            if (evente.CheckSysEvent(globalEvent)) 
            {
                eventset = true; //This is just to check with debugger if the event was raised elsewhere
            }
            Thread.Sleep(100);
        }
    }
}

And in the Button handler just do

private void btn_runThread_Click(object sender, EventArgs e)
{
    threadClass = new Name(globalEvent);
}

Mind that there are still a good portion of mistakes and ooopsies, but at least, this will keep your GUI thread running and you may gain an understanding to go on from here.


A totally different approach (if you are willing to consider it) would be to use a System.Windows.Forms.Timer instead. With that you can have a method called every X time, which would check the state of the globalevent as you are trying to get the thread to doing. The timer, however, makes this a little more convenient.

Fildor
  • 14,510
  • 4
  • 35
  • 67
  • Thanks a bunch! I was really stuck. Will try this and use this as a base to get going like you said. – Rakku Jan 08 '21 at 15:46
0

The typical way would be to create the thread in the constructor, as described in the answer by Fildor.

But I want to point out that using the Thread object directly is rarely the correct way to do things since there are other tools more suited for whatever you are tryibng to do:

  • If you want to do something compute heavy on a background thread once, and update the UI after it has been done. Use Task.Run and async/await
  • If you want to do something every X seconds. Use a timer. There are both timers that run on the main thread or a background thread, see differences between timers.
  • If you want to run an compute heavy operation in parallel, use Parallel.For, possibly in combination with Task.Run.
  • If you want to call IO intensive methods without freezing the UI, use async/await in combination with the appropriate Async methods.
  • If you want to create a producer/consumer or other processing pipeline there is the DataFlow library
JonasH
  • 28,608
  • 2
  • 10
  • 23