In a recent
Keyboard / Switch Matrix - H/W Debounce + Multiplexer thread, both polling (dynamic scanning) and interrupt methods for detecting button presses were discussed; and the benefits and downsides of each.
I use state machines extensively to implement small-screen menu systems on embedded targets. I keep the state descriptor structures in Flash, so that very little RAM is needed to implement the actual menu. (Think of something like a 0.9" OLED display with 128x64 pixels or 16×4 characters, with a couple of buttons for changing entries, and one to three buttons for selecting and entry and possibly changing the value associated with the entry.)
I also often use ELF object file format features (especially sections) to gather such menu entries (albeit in an unspecified order) across many source files into a single data structure. Not perfectly portable, but all the targets and toolchains I'm interested in use ELF object files during the build stage.
In pure software projects, state machines are extensively used in lexers and parsers; typically via Lex or Flex (that generate state machine based C code for lexical analysis as needed for parsers, interpreters, and compilers).
My scientific specialty is computational material physics toolmaking, i.e. simulators, visualizers, sonifiers, etc. In Linux, I use both symmetric multitasking (running code on similar cores, typically CPU cores) and asymmetric multitasking (running code on different types of CPU cores, or on both CPU and GPU cores), but also distributed computing (with code running in parallel on physically separate computers and communicating with each other). I could rant for hours about this, especially how very few tools currently handle the communications and computation in parallel, asynchronously; instead, they typically compute first, then communicate, and so on, wasting resources and taking more wall clock real time than would be necessary, because us programmers are not that good at this stuff.
I don't use RTOSes: on the electronics side, I'm just a hobbyist, and it is easier and cheaper for me to combine a Linux workstation communicating with USB to a dedicated microcontroller that does the realtime stuff, with their combined system achieving what I want. I like to use Teensies for the microcontroller stuff, especially since an USD $20 Teensy 4.0 supports high speed USB (480 Mbit/s), and I can trivially achieve > 200 Mbit/s (25+ Mbytes/s) even using USB Serial (USB bulk transfers are even better).
For that reason, I don't write my own schedulers. I do sometimes use postponed work queues, where an interrupt signals work that needs to be done (but immediately returns, keeping the interrupt-caused jitter to a minimum), but some kind of main loop handles that work. In hosted (non-embedded) environments, especially in Linux, I just let the kernel schedulers do their thing, and just make sure I don't make assumptions about scheduling (so that the end user can manage the CPU and I/O priorities themselves as needed).
I do not
select the method or approach. I evaluate several, often letting my subconscious chew on it during I sleep, and then test them using small test programs that verify the core logic and/or operations. I have literally thousands of these, although I only keep the most recent couple of hundred at hand. Each one is in their own directory, with either the test program describing the thing, or with a README and associated text/Maxima/Maple/Octave/PDF files describing the thing. I often find stuff in these using
grep -e pattern -R root-of-my-test-program-tree/.
In other words, I'm not so smart that I can offhand say what method to use for a given program: I must think about it, and work it out, for each case. I'm often wrong, too, and discover an even better solution a few days later than my initial suggestion.
After over a couple of decades experience and having done all sorts of different programming stuff –– that's why I have so many of them, and typically add several each week; about 80-120 per year ––, and having tried many thousands of different approaches, I do have
some experience to guide my initial guesses, and base my approaches on. There are greybeards here whose experience vastly,
vastly, exceeds mine; but because after the initial learning curve, we all tend to approach things as "there is no single correct answer, and knowing different solution methods to
this problem is useful because similar things crop up all the time", we can and do discuss these things as colleagues –– or even co-conspirators in sneaky/efficient problem-solving. (As opposed to the woefully common, "let's throw this stuff at the wall and see what sticks" approach, where developers write code that seems to work but even the developers cannot tell how or why.)
This is something even a new programmer can do. The point is to keep ones mind open, and realize that there is no single correct answer: almost everything is a balance of various factors, and subject to change when the context changes. We learn continuously, every single one of us, and never ever know "enough". Some say to keep humble, but as you can see, I'm not very humble here; it's just that the world is vast, and us humans small.
Thus, describing or presenting ones problem to colleagues to consider or help with, is a very, very important skill. Typical error is describing only the problems related to the currently selected solution method, instead of providing the background of the underlying problem that is to be solved, and the reasons this solution approach was chosen, and only then the "surface problem" encountered along that solution approach. Listing attempted alternatives helps shortcut the discussion, although then it is important to also include the reason why each rejected alternative was rejected. Being systematic and analytic here is paramount.
If the problem is one that hasn't already been discussed thoroughly, and does not have oodles of online resources to read to discover solution approaches for, a well-formed question typically attracts even the old mages and greybeards. Not just here, mind you; also in academia and professional work.