When writing an interface to hardware over a communication bus, communications timing can sometimes be critical to the operation of a device.
As such, it is common for developers to spin up new threads to handle communications.

It can also be a terrible idea to have a whole bunch of threads in your system, an in the case that you have multiple hardware devices you may have many many threads that are out of control of the main application.

Certainly it can be common to have two threads per device, one for reading and one for writing.

I am trying to determine the pros and cons of the two different models I can think of, and would love the help of the Programmers community.

Each device instance gets handles it's own threads (or shares a thread for a communication device). A thread may exist for writing, and one for reading. Requested writes to a device from the API are buffered and worked on by the writer thread. The read thread exists in the case of blocking communications, and uses call backs to pass read data to the application. Timing of communications can be handled by the communications thread.

Devices aren't given their own threads. Instead read and write requests are queued/buffered. The application then calls a "DoWork" function on the interface and allows all read and writes to take place and fire their callbacks. Timing is handled by the application, and the driver can request to be called at a given specific frequency.

Pros for Item 1 include finer grain control of timing at the communication level at the expense of having control of whats going on at the higher level application level (which for a real time system, can be terrible).

Pros for Item 2 include better control over the timing of the entire system for the application, at the expense of allowing each driver to handle it's own business.

If anyone has experience with these scenarios, I'd love to hear some ideas on the approaches used.

1 Answer
1

1. Active Object
If you take up the first approach - it is further possible to encapsulate the existence of thread "behind" the object (or context) and API still remains accessible for Application to be able to query and communicate. This is called as Active Object pattern. The primary read/write calls that application makes can be made blocking or non-blocking depending on requirement. However, making it non-blocking is where Active object pattern is a must.

2. Thread Pool
On the other hand, if there is a serious constraints on exact number of threads than an alternative pattern can be used called as "thread pool" pattern. This method requires that tasks needs to be clearly abstracted out and queued. Whenever a working thread finishes, takes next best task from the queue.

3. Asynchronous I/O
The third pattern is called Asynchronous I/O where which shows least coupling between IO performing tasks and the application.

Active object makes life of application quite easy. Where as thread pools is about disciplined execution. Asynchronous I/O is essentially to minimize all communication overhead (or rather assumes minimal communication).

There is a lot of literature on each of this subject so i am not going very long here - however, i would add if there are specific queries.

While, some of the pointers i have given are in JAVA, the patterns are pretty much implementation in C or C++.

These patterns are grounded on firm and validated principles (like most design patterns) that should help bring good focus on the design.