2

Working on a IoT device (Linux, ARM, C++). Right now I am creating boilerplate code and general program structure. The program communicates to different hardware:

  • GPIO pins on the board.
  • Several 1-wire sensors.
  • Additional GPIO chips and keys on the I2C bus.
  • Device connected via RS232 interfaces

Now the tricky part is that RS232 devices, 1-wire and I2C devices are not that quick to respond. It means that if I do something like this on a single thread:

    while (!_isShutdown)
    {
        for (auto& device : _devices)
        {
            device.update();
        }
    }

I would waste a lot of time and resources, even more so, I would unable to complete my task in time because several sensors may take up to 500ms to retrieve the latest value.

This issue naturally led me to multithreading. I can a background thread for each device. It means that the more devices I have, the more threads and memory (I believe 1MB) I would use. In my case it may take up to 30 threads just to manage hardware (I have a network communication as well which means even more threads).

I thought that maybe there exist a better approach for hardware management. Maybe creating a thread each time a read execution blocks is a bad design? If so, what are the alternatives? There are many simple examples on the Internet, but few useful materials on complex projects.

On devices like Raspberry memory and execution speed is not an issue, but that could to a bad habit of developing stupid and unoptimized code. I ask you to clarify this.

CorellianAle
  • 645
  • 8
  • 16
  • 2
    If the devices are abstracted behind a file descriptor interface and are accessed with common file `read` and `write` calls, consider using `select` or `epoll`. They allow you to run multiple simultaneous IO operations all in one thread. – user4581301 Dec 12 '17 at 19:03
  • 1
    Way down at the bottom of http://www.tldp.org/HOWTO/text/Serial-Programming-HOWTO is a decent example of using `select` to read two serial ports. – user4581301 Dec 12 '17 at 19:12

2 Answers2

1

You did not explain exactly what a "better approach" is, from your point of view. Since as you said, memory is not an issue, then providing each device with its own thread can be not that bad. Despite excessive memory consumption, such code can be easy enough to read and debug.

To avoid memory consumption, you may want to switch from blocking code to unblocking. That is, a thread does not wait the end of I/O operation, but hardware itself notifies of the end of operation, e.g. with a hardware interrupt. Interrupt handler then sends a message to its listeners, which can cause scheduling of tasks to run on a thread pool. The most mature task library in C++ is Intel® Threading Building Blocks. Programming in tasks can seem more complicated rather than in threads, but is more fun as can result in code more adequate to the desired goals.

Alexei Kaigorodov
  • 13,189
  • 1
  • 21
  • 38
  • I totally missed a `select` approach. I guess `select` for I/O reads combined with the library you've mentioned (for more high-level logic) is the final answer. – CorellianAle Dec 13 '17 at 07:16
  • By the way, do you have any opinion on the Microsoft's pplx task (as part of cpprestsdk)? – CorellianAle Dec 13 '17 at 07:18
  • Never heard of pplx task before. Read this: https://stackoverflow.com/questions/7515097/what-are-the-differences-between-intel-tbb-and-microsoft-ppl – Alexei Kaigorodov Dec 13 '17 at 16:58
0

From my experience, the most flexible and change-resistant approach is to use threading: for each bus network interface there is different thread. On top of that I use Activity design pattern, where I will submit an object as event handler to another thread in safe way. Some of it can be read from here: http://tauria.ee/002/products/gradislib/

Peeter Vois
  • 66
  • 1
  • 4