Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Listening for events in background thread

I want to start a thread that listens for events and handles them in the background. This is what I've done so far:

private Thread mThread;
private bool mKeepHandlingIt;

private void Init()
{
    mKeepHandlingIt = true;
    mThread = new Thread(ProcessEvents);
    mThread.SetApartmentState(ApartmentState.STA);
    mThread.Start();
}

private void ProcessEvents()
{
    StaticClass.CoolEvent += HandleIt;
    StaticClass.StartDoingStuff();

    while (mKeepHandlingIt)
    {
        Application.DoEvents();
    }

    StaticClass.StopDoingStuff();
    StaticClass.CoolEvent -= HandleIt;
}

private void HandleIt(object sender, EventArgs e)
{
    //Handles it
}

protected override void Dispose()
{
    if (mThread != null)
    {
        mKeepHandlingIt = false;
        mThread.Join();
    }
}

Is there a better approach to this? Like a thread class that would be better suited for this purpose? BackgroundWorker did not seem like a better choice in this case...

If this is a good approach, though, then I have a problem that I can't quite understand. The above solution works fine for listening to events and handling them, but when Dispose is called and mKeepHandlingIt is set to false, the while loop IS exited (debugger doesn't break if I place a breakpoint in the loop) but no code after the loop is executed and mThread.Join never returns. Basically... what I want to know is: how do I stop the thread and then make sure not to continue before it has cleaned up?

like image 835
moggizx Avatar asked Feb 16 '23 02:02

moggizx


2 Answers

There are many things wrong with this code. First off, it suffers from the illusion that the event handler runs on that worker thread. That's not how events work, the handler runs on the exact same thread as the code that fires the event. There is no mechanism in .NET to let it hop to another thread.

Something you can see with the debugger's Debug + Windows + Threads window. Set a breakpoint on the Init() method and one on the event handler. When they break, switch to that debugger screen and note the thread that's selected.

Using Application.DoEvents() is very dangerous. But not here, the thread hasn't created any windows so there are no events to do. Instead, the thread will just burn 100% core and not accomplish anything at all.

The mKeepHandlingIt variable does not in general do what you hope it does. When you run the Release build of your code on a 32-bit machine then thread will never see it being set to false and the thread will thus never exit. And Thread.Join() will deadlock. This is caused by the jitter optimizer, it stores the bool variable in a cpu register and will never reload it from memory. You must declare the variable volatile to prevent this optimization from being made. Which is a hack, you should always use a proper synchronization object to signal threads, an AutoResetEvent is appropriate here.

Just delete this code, it doesn't help you.

like image 181
Hans Passant Avatar answered Feb 26 '23 09:02

Hans Passant


For most cases you should never use doevents. s.

For example see:

  • is-doevents-evil
  • use-of-application-doevents

Creating Threads with new Thread can run into trouble if no more threads were aviable. Therefore you could use the Threadpool.

But I would stronlgy recomment to have a look at Tasks and the TPL

Then you can start with

Task.Factory.StartNew()

There als awaitable when usinc the async libraries and you have functions like continuewith

see also MSDN await one ore more tasks

            // Wait for all tasks to complete.
            Task[] tasks = new Task[10];
            for (int i = 0; i < 10; i++)
            {
                tasks[i] = Task.Factory.StartNew(() => DoSomeWork(10000000));
            }
            Task.WaitAll(tasks);

Async and await also don't necessarily use threads but work asynchronous for this see: async-await-vs-threads

This is can be a great benefit to safe ressources. Especially when you have io operations.

Also a more complex way is to implement the listenings in a seperate process with some kind of servicebuses (what ever you want to achieve)

like image 22
Boas Enkler Avatar answered Feb 26 '23 08:02

Boas Enkler