Threading for application sending results/updating at a very rapid rate and in high volume

Hello all,

I am working on adding threading for updating data aside from the UI in an application I am making.

Under “6. Notes about this example” on this page: https://wiki.wxpython.org/Non-Blocking%20Gui, it says that:

Using events to pass back data will work well in most cases but if you have a or many worker threads that send results at a very rapid rate and in high volume there are better solutions to improve performance and reduce overhead. As handling the high volume of events can once again lead to blocking on the main thread. These other approaches are also not very difficult, I may add a supplemental example at some time if there is interest.

I am wondering how to implement this for high volume of events?

EDIT: Right now I am using wx.PostEvent but the program seems to crash when the worker thread can’t keep up with the events.

It seems that it would be best to stop the data processing in the background halfway through the thread working if there is another event rather than continuing to process before handling the latest event (similar to “bounce” for data entered into an input calling to a web API) but I am not sure how to implement this with wxPython, or if this is the best way to handle this.

Below is what I am doing now:

Thread.py

from threading import *

import wx

# Define notification event for thread completion

EVT_RESULT_ID = wx.NewIdRef()

def EVT_RENDER_RESULT(win, func):

    """ Define result event """

    win.Connect(-1, -1, EVT_RESULT_ID, func)

class ResultEvent(wx.PyEvent):

    """ Simple event to carry arbitrary result data """

    def __init__(self, data):

        """Init Result Event."""

        wx.PyEvent.__init__(self)

        self.SetEventType(EVT_RESULT_ID)

        self.data = data

class RenderThread(Thread):

    """ Thread class that executes processing """

    def __init__(self, parent):

        """ Init the worker thread"""

        Thread.__init__(self)

        self._parent = parent

        # Starts the thread running

        self.start()

    def run(self):

        """ Run the worker thread """

        # Code executing in the new thread here.  My actually code here is compositing images with Pillow, etc.
        render_image = # do my stuff here

        # The result returned
        wx.PostEvent(self._parent, ResultEvent(render_image))

Main application code:

class MainApplication(wx.Frame):

    def __init__(self, arguments):

        wx.Frame.__init__(self, None, title="", size=(1000, 800))

        # Indicate we don't have a worker thread yet

        self.worker = None

        # Set up event handler for worker thread results

        EVT_RENDER_RESULT(self, self.OnResult)

   ...

    def OnResult(self, event):
 
        # Update my stuff here and get the result...
  
        # The worker thread is done
        self.worker = None

    def Render(self):

        """ This is the method that is actually called when something is updated

        """

        # Update stuff....

        self.worker = RenderThread(self) # From thread.py

When render is called too much in a short period of time, the program seems to crash.

Any help is greatly appreciated. :slight_smile:
Thank you.

When you have such a high frequency of events and want to update your GUI on each one, then there is no point of moving the data generation to a separate thread. You could do it in the main thread as well.

You should decouple data generation and GUI update.
E.g use a list (owned by the main frame.
The worker would append the data to this list.
The main frame would check regularly whether there is fresh data and e.g. use pop(0) to consume it until it’s empty again. This could be done in an EVT_IDLE handler or with a timer or the worker thread could post an event from time to time.

Depending on the needs there other data structures and also synchronization tools like threading.Lock() .

Hello and thank you for your comment. :slight_smile:

Your suggestions are helpful. Especially about decoupling data generation and the GUI updates.

My project is Gimel Studio. I have been trying to figure out the best way to implement a “bounce” type event system for the renderer. (Most of the relevant files are in src/GimelStudio/renderer.)

The current implementation works just fine normally (if the user is not too “hard” on it and waits until the image is rendered each time). However, at certain times and with larger images/node graphs it crashes.

The problem seems to be that the thread can’t keep up with the updates because the worker thread continues to complete each render even though many more events have been sent (so it basically gets behind).

I originally did have the rendering on the same thread as the GUI, but then the UI freezes while rendering (of course). I am sure this would work if the data computation happened quickly, but the rendering usually takes around 1 sec. So, I am not sure that this is the right way to handle it.

I guess I am not really sure what to use in my case.

I haven’t really done much threading before the last two implementations of this renderer for this project (but I am happy to learn!).

Any ideas?

I’m not sure if it is a good fit for your application, but something to consider is to provide a way to drop intermediate results between the previous render and the current moment. In other words, when the UI goes to redraw itself it chooses to only do the current state of the data model, even if there have been multiple updates of the data since the last time.

I think your problem is not the high rate of events, but the number of threads that you’re generating.

Let’s check whether I got your use case right from looking at the screenshots and description.

You have e.g. a chain “Image -> Opacity xx%-> some other operations”.
So when the user changes Opacity from 100 to 50% using a spin control, you are starting 50 threads? This could happen at quite a high rate if the user is using a mouse wheel.
Each of the threads is first calculating the interim result with the new opacity value and then more results.

You should:

  • start not too many worker threads; e.g. use a delay - if in the meantime the opacity spin control has been changed again, no thread for the interim setting(s) needs to be started
  • stop the worker thread when the result is not required any more

For stopping a thread, you could give it a flag “canceled” that would be checked regularly.

If you can ensure that this flag is checked at least every 100ms or so, then you could use a better approach. You could use a single worker thread with an endless loop (mainloop). This would receive commands “start” and “cancel” from the main thread inside the loop.

if you don’t spread your rendering over all kernels (and most PCs nowadays have more than one of them) I dare say you’ll never become competitive; and that means you’ll have to take a dip into multiprocessing…

Hi @Robin , :slight_smile:

Thank you for your comment. This is actually what I was considering. In addition, only rendering the nodes that have changed values (forward in the node tree). This is what I meant about “bounce”.

For instance, I was trying to work on an “add text” node, but the input wx.EVT_TEXT event (obviously) updates the value (and thus the renders) every time a character is entered (including a backspace). This isn’t necessarily bad, but it does overwhelm the thread that is doing the rendering and the program crashes (not very gracefully…).

Hi @DietmarSchwertberger,

I think your problem is not the high rate of events, but the number of threads that you’re generating…So when the user changes Opacity from 100 to 50% using a spin control, you are starting 50 threads?

The current implementation only uses a single other thread to do the updates. The problem is that this (poor) thread gets overwhelmed with the updates it has to do.

All of your comments (which I am thankful for :slight_smile:) do indeed seem to confirm my thought that I need to implement a “bounce” type feature which delays to see if another update is coming then only renders all the way through when a value is not changed during that time.

This is a good idea. Thank you.

The only thing I can see is that it seems that threads need time to die. Correct? So, what would be a way to work around that? (Again, I am not really 100% learned on threading so please forgive me if that is not correct…)

@da-dada, you are certainly right about looking into multiprocessing. I was considering going to that before, but it seems like a good idea in light of this.

Thanks again.

The point is not to create and stop threads if you can ensure that your calculation can be stopped within a short time.

See this (untested) example code.
The thread would run forever.
The ‘calculate’ method would terminate when the main thread calls ‘start’ again.

class WorkerThread(threading.Thread):

    def __init__(self, ...):

        self.start_command = threading.Event()
        self.lock = threading.Lock()
        self._arguments = None

    def calculate(self, arguments):
        with self.lock:
            self.cancel = False
            self.start_command.clear()

        # do some useless work and check whether to continue
        for n in range(1000):
            if self.cancel:
                return False  # canceled
            time.sleep(0.01)
        return True  # finished

    def start(self, arguments):
        with self.lock:
            self.cancel = True
            self._arguments = arguments
            self.start_command.set()

    def run(self):
        while True:
            self.start_command.wait()  # blocks until self.start_command.set() is called
            if self.calculate(self._arguments):
                # finished -> send result to GUI thread
                # wx.PostEvent(...)
                # wx.CallAfter(...)


The same pattern can be used for multiprocessing.

spreading the workload over several cores is extended by tiled renderers and that handed through to the GUI which gives the user the illusion the rendering would be a continuous stream; what you are describing is an asynchronous flow and that is provided in Python by the asyncio module: async/await is all handled automatically in that event loop (there are other python compatible solutions for that); to spread that loop over more than one core is, of course, not possible in python!

Thank you. :slight_smile:

I will certainly try this kind of implementation and see how it works.

Yes, my understanding is that you would have to probably split the image into chunks (as a tiled renderer, similar to Blender 3D, etc), do the processing and combine them afterwards.

Thank you. :slight_smile: