Qt World Summit: Register Today!

Thread design recommendation [SOLVED]

  • Hello all,

    We've made the commitment to use Qt for our project development. I've spent several weeks now getting to know various pieces, running examples etc and I thought I might ask some of the experts here based on their knowledge and experience with Qt what might be the best way to proceed based on my needs.

    This will be a "desktop" application which will initially run under Windows 7 32 bit. The app will interface to a device which will mostly be USB using FTDI D2XX drivers/lib. There will be most likely 4 serial devices in addition to the USB I/O.

    Basically everything on the device is somewhat interrelated meaning that you can't use one of the serial port devices unless some other things are in position (using USB commands). So there is some sequencing.

    Since it is all interrelated I was considering a "device" model based around the controller threaded/worker object concept shown in the Qt examples. I was considering that each I/O (serials and USB) would in effect have a thread. So I'd have 5 threads each with a worker object that would signal back and forth to the controller. The controller would in effect represent the main device for the most part.

    My reason for thinking about using threads and worker objects for each I/O device is there is some need to "do and forget". IE I in some cases need to transmit a command but can't block the caller. Later when the command generates some response I'm expecting the worker object would signal the controller and send the response data.

    In some cases I would expect the worker objects/threads to be mini state machines but for the most part that logic of how the instrument operates would be at the fixture or controller level. I would perhaps think the threads/worker objects might be somewhat idle (looping/yielding) until given work to do via a signal from the controller.

    The serial I/O stuff will be fairly slow and the devices the serial ports will connect to are physical devices that take time to operate. So my code needs to be able to transmit a command and come back later and see if it was successful and if so deal with the response. This also leans me towards the thread / worker approach.

    The USB I/O however will be extremely fast and I expect that there will be like a queue of operations being sent to the USB I/O on a continuous basis. Some of it involves reading temperatures. So I imagine that worker object will be pretty busy cycling through various things to do.

    Again my typical approach to this kind of stuff is to create a "fixture" object that represents as closely as possible the real world device. As I said above it is somewhat sequential in nature. IE you cannot do command/operation C until you've successfully done commands A and B.

    So my question for the experts and I know this is somewhat vague and I apologize, I'm not a liberty to give much more data at this time, is recommendations based on how Qt handles threading and signals/slots and if the concept of a "controller" and multiple threads/worker objects makes sense?

    I do realize there are lots of design patterns out there. I'm interested in recommendations. Based on my experimentation with Qt it seems like I can accomplish almost everything I think I need with the controller threads/worker objects concept.

    Next general question is when does it make sense to use thread pool over just having 5 thread objects and instance vars for them?

    I've also looked some at QtConcurrent and I'll study it further but I wonder if it better handles my need?

    Thanks in advance. Trying to get my head around the best way to approach this thing.

  • I wouldn't bother with a thread pool. I don't think (based on what you said) that you are doing enough to need multiple threads per device. Unless I misunderstood that part.

    The Qt threading stuff will work great for your needs.

    Qt signals/slots would probably handle things just fine. But you said some places are slow communications in which case without a thread you would lock up the GUI.

    You can basically have an object you assign to a QThread. That objects can have signals/slots attached to it. This will handle your threading automatically. Then you can just do something like emit myStartSignal(); and setup a handler to catch the return signal of somethingDone(). So your threaded object would emit somethingDone() when the task was complete. And you register to catch that signal on a slot where you need it. Something like:

    connect(myThreadObject, SIGNAL(somethingDone()), this, SLOT(myProcessSlot()));

    Basically it sounds like Qt handles everything you need in a very easy manner.

  • That is my feel so far as well. Thanks ambershark for the reply!

    My current design thought is something like:

    USB worker object/thread
    Serial worker object/thread
    Serial worker object/thread
    Serial worker object/thread
    Serial worker object/thread

    So my app (and other potential clients) would interact with fixture. Fixture would handle the logic and sequencing needed to run the device and would have data/methods so my app and others can know what is happening, the status of devices, temps, etc.

    Obviously there will be signals/slots between the workers and fixture. Then fixture will have some signals it can emit to notify watchers of things. Fixture would also have some methods to call like Init() might coordinate the process of setting the instrument up.

    Some of my serial devices are RFID reader/writers so I'd imagine that fixture would have some methods like: WriteTag() which would know how to signal the worker/thread for the correct serial device to write the tag etc.

    Anyway I'm going to mock up a prototype of this with some of the threads responding with some dummy data for now just to see how it all feels.

    One question I have is who owns what? IE I'm going to need 4 QSerialPorts. Are they constructed and owned by my fixture or should the worker objects that deal with them construct and own them?

    I'm sure some of this will come more clear as I bump into the various issues.

    Thanks again for the reply.

  • The serial ports would be in your thread object. Any thing created or parented to the thread object will be on that thread's message loop.

    You want to keep worker thread stuff off your main thread.

    For instance if you had:

    class MyObject : public QObject

    MyObject(QObject *parent = 0) : QObject(parent) { }

    void resultReady();

    private slots:
    void myResult();

    // ...

    QThread *thread = new QThread(this);
    MyObject *obj = new MyObject();

    connect(this, SIGNAL(doSomething()), obj, SLOT(startSomething()));
    connect(obj, SIGNAL(resultReady()), this, SLOT(myResult()));

    emit doSomething();

    Using that example myResult() would be called when the result of the thread running happened.

    It can be a lot more powerful than this. You can subclass QThread but this is just a simple example to get you started on threading. The part after the // ... comment is in another QObject based (so derived from any Qt class pretty much) class. It should have a doSomething() signal.

  • [quote author="ambershark" date="1420501306"]The serial ports would be in your thread object. Any thing created or parented to the thread object will be on that thread's message loop.

    You want to keep worker thread stuff off your main thread.

    I generally understand all of that. But I want to make sure I understand the serial port owner. I was thinking it would be the worker object so something like:

    @class MyWorkerObj : Object
    QSerialPort serPort;

    .... with work function etc....

    Then in controller or fixture like you have it basically:

    @QThread *thread = new QThread(this);
    MyWorkerObj *obj = new MyWorkerObj();

    connect(this, SIGNAL(doSomething()), obj, SLOT(startSomething()));
    connect(obj, SIGNAL(resultReady()), this, SLOT(myResult()));

    Then when MyWorkerObj runs its work method or is created perhaps it would setup the serial port and then use it of course.

    Am I following what you mean correctly?

  • That would only work if you make QSerialPort serPort a pointer and allocate it after the MyWorkerObj is moved to the thread.

    class MyWorkObject : public QObject
    QSerialPort *serPort;

    void MyWorkerObj::startSomething()
    if (serPort)
    serPort = 0;

    serPort = new QSerialPort(this);

    That would ensure that serPort is part of the QThread *thread you moved MyWorkerObj in to.

    EDIT: oh and startSomething() is a bad place for overall initialization of variables. I would pick a different signal or I think QThread::start() emits something you can use. Can't remember off the top of my head.

  • Ok got it. Thanks so much!

  • No problem, happy to help.

    You made a great choice with Qt. Been using it for 15 years now and it's just awesome.

    Every time I have to do a project no matter how small without Qt I get grumpy cause I know it's just gonna be harder and a lot more work. :)

  • 15 years... Nice! Well I have a long history with Borland/Embarcadero products and my client really wanted this in C++ Builder but frankly the support is just not there. Few if any active forums and the compiler seems out of date.

    While they have some cool things I just couldn't feel comfortable that code written specifically over that platform would be well received when I finally hand it over to the client (which I must do as part of this work).

    I think Qt is popular enough, supported well enough that my client will be extremely happy to receive it based on this framework.

    One last question regarding your Edit. I agree that startSomething() is really not the best place to initialize vars. Why not the constructor of MyWorkerObj?

  • The constructor won't work because the object is constructed in the main thread before it is passed off to the qthread with moveToThread.

    So you'll need a signal to init once the thread is started.

  • I actually figured that out AFTER I posted... duh... Thanks again!

Log in to reply