Back when I was a kid, learning about programming in college, I was left with the impression that parallel computing/multi-threading is not easy. 1. We learned about race conditions, deadlocks, semaphores and all that. Once I started working, the idea that multitasking is hard was reinforced by the absence of it in our CAD/EDA tools which surely would have benefited from the effort. 2
In recent years, my views on concurrency have finally changed a bit, first via the simplified threading model that Perl provides and more recently through my Android development adventures.
A couple years ago, I supported a feature that required nightly crawling of a fairly large directory structure, a three deep tree with ~10,000 files each of which needed to be read and parsed. Normally, it would be ok for stuff to run for an hour or two, but in this case, I had to stop the service that depended on the tree until processing was finished. So I needed to parallelize.
When I read a bit more about Perl’s threading model, I was surprised to learn that it’s mostly just forking, except it’s easier to pass data structures between threads/forks. 3. So I created 10 threads, each of which read it’s own subtree and passed back the results of parsing 4
The crawler utility wasn’t rocket science, but it did open my mind’s way of thinking a bit. Multi-threading doesn’t always have to be fraught with danger if you just limit the amount of shared data. Threading may be intimidating, but forking is easy. There are many tasks that lie somewhere in-between.
With Android development, I’ve been forced to open up a bit more. Android doesn’t allow network calls from the main UI thread. You have to do it in a separate thread. Android doesn’t allow UI updates from anywhere other than the main thread, so I can’t just do everything in my side thread. 5
As I’ve worked on my SMPTE app, I’ve found concurrency is a nice way to divide the problems I’m trying to solve:
- the main UI thread. Update the display, respond to buttons
- a thread that other phones can talk to for syncing. Two+ actually. One waiting for socket requests and one for each accepted connection
- a thread that reaches out and syncs to a master phone.
- a thread that computes the current timecode.
- I imagine I’ll have a thread for generating the SMPTE audio signal.
There’s very little that needs to be communicated between these. It’s really just the stored time adjustment value. In a way, even though I’ll have a bunch of threads, since there’s only one long that is shared, it’s really a bunch of almost independent processes.
did people use the term multi-threading back then? ↩
I do remember that my friend Trey tried to add it to our signal router with mixed success. I don’t believe that effort ever made it into production ↩
forking usually requires you to setup a socket and serialize all your own data. This isn’t hard but I never took the time to write a series of helper routines. ↩
Most of the old slowness was due to the loop: ask for a file, wait for the fileserver, process quickly, ask for another file, wait some more ↩
I also found it interesting that garbage collection in Java happens in its own thread. ↩