Say you want to make an app that, details aside, basically does the following:
- It continuously reads data from [something], could be Bluetooth or a serial port or a socket or anything, at a relatively constant rate.
- It visually formats that data as it comes in and then displays it [somehow], updating (ideally) fast enough to pass the flicker fusion rate and appear continuous.
So as a concrete example, you might have an audio visualizer, reading in 88,200 bytes/sec and plotting something that makes you look like you're on acid.
This general use case leads pretty fast to some big-picture paradigmatic questions about how JUCE handles concurrent programming, and I'm interested in hearing what Jules has to say about the "best" ways to use JUCE for this sort of situation.
So one basic and general way to do it is to use Threads, something like this (without too many details):
- A Thread for Reading - reads and buffers from the port at a constant rate
- A Thread for Processing - slices and dices the data lots of times per second, doing so in its own thread to make sure it doesn't interfere with UI handling
- A Thread for Plotting - this is actually just going to be the Message thread, I believe, since that's where JUCE does graphics stuff
Pretty basic. Then you have to deal with safely passing data between the three threads, signaling, etc. You might have a shared blob of memory with CriticalSections locking it, and then have threads use wait() and notify() and etc to signal one another and all that. This might be straightforward or an unbelievable pain in the ass, depending on the complexity of your application.
What I'm interested in is in seeing how well JUCE's other way of handling concurrency, the Message loop, enables us to get away from the above. The equivalent of the above would be the following:
- A MessageListener for Reading - reads from the port and then sends a Message to the Processing actor when its internal buffer has attained sufficient critical mass to warrant it
- A MessageListener for Processing - responds to Messages from Reading, processes the data in each Message, sends a new Message to Plotting
- A MessageListener for Plotting - plots
I'm envisioning MessageListeners as basically taking the roles of "actors" from the actor model. I really like the idea of it conceptually.
However, is this use case something the Message loop is built to handle?
It does mean the Message loop is going to get bombarded with lots of messages going back and forth between the actors. You can counteract this somewhat by controlling the granularity of messages being passed, like only passing messages every ~1/20th of a second or so with larger data lumps. But, in general, it's still a lot. Also, all of the DSP from that second actor is going to be done in the Message thread rather than its own thing, which might block stuff.
Would it slow down UI handling? What are best practices here? I'd love to just never use Threads again. However, I can't find much info on the intended uses for these things - can anyone help out?