Worker thread and QML exposed QObject best practises



  • Hi,

    I need to wrap a C++ 3rd party library into a qml plugin in order to use it easily from qml.
    This lib does heavy work so I have to run it from another thread.
    I did create a QObject based class MYCLASS with some Q_PROPERTIES and Q_INVOKABLE methods that will be exposed to QML.
    Within that class I instantiated a worker object MYWORKER and moved it into a qthread. This worker object make calls to the 3rd party lib.

    For Q_INVOKABLE methods of MYCLASS I do forward calls as signals to MYWORKER class.

    Now for MYCLASS properties I'm wondering what is the best way to sync MYCLASS and MYWORKER.

    Should I duplicates each members in both classes and use signals/slots mechanism to sync it OR should I implement the members within the worker class (as PIMPL) and use a mutex for every read/write on members ? Or is there another clever way?

    Thanks in advance for your advices


  • Moderators

    I recommend singals/slots. This way you are guaranteed that no thread synchronization problems occur - all connections will automatically use Qt::QueuedConnection path and there is no danger of deadlocks etc.

    Actually, since your MYWORKER is a QObject, why do you need MYCLASS at all? Won't it be better to connect MYWORKER into QML directly? If you only use signals and slots (not a Q_INVOKABLE), the communication will be queued. And you save a lot of typing by removing the middle man (MYCLASS).

    Hm, unless QML engine expects QObjects to be in the same thread... not sure, you would need to test it first.



  • Thanks for the advice, actually I did try both solution and I think that, as you mentioned, the signal/slot approach is less error prone since queued connections are handled automatically (Qt magick's) but on the other hand it means more data copy since you need to have members in both classes.
    All considered, since my members are basic type (int , QUrl, QString) maybe the signal/slots approach is more suited, what do you think?

    Regarding exposition of a QThread based class directly to QML, it is not possible since I need an event loop a my thread.


  • Moderators

    @X-Krys said in Worker thread and QML exposed QObject best practises:

    [...] as you mentioned, the signal/slot approach is less error prone since queued connections are handled automatically (Qt magick's) but on the other hand it means more data copy since you need to have members in both classes.
    All considered, since my members are basic type (int , QUrl, QString) maybe the signal/slots approach is more suited, what do you think?

    Please rephrase that question. You first say signal/slot way is less error prone, then ask again if signal/slot is more suited... I'm not sure if you wanted to ask something else or just repeated same question twice ;-)

    Regarding exposition of a QThread based class directly to QML, it is not possible since I need an event loop a my thread.

    Well, no problem there...

    // Pseudocode, "brain to terminal" :-)
    QThread someThread;
    MYWORKER *worker = new MYWORKER();
    
    worker->moveToThread(&someThread);
    someThread.start();
    

    And then pass worker to QML. You don't need to expose the QThread anywhere.



  • I said that signal/slot is less error prone BUT behind this approach it involves a copy of each members that are needed to be shared between the two classes. So considering this penalty I'm wondering if the approach is still ok.

    Regarding your idea of passing the worker to qml, note that I'm developing a qml plugin and so I can't pass an instance to the qml engine (and I don't want singleton), I have to qmlRegister a class that can then be instantiated from qml.


  • Moderators

    @X-Krys said in Worker thread and QML exposed QObject best practises:

    I said that signal/slot is less error prone BUT behind this approach it involves a copy of each members that are needed to be shared between the two classes. So considering this penalty I'm wondering if the approach is still ok.

    As long as you use COW (implicit sharing) classes (most classes in Qt are implicitly shared), no actual copying takes place, only the refcount is incremented when you create a "copy".

    Regarding your idea of passing the worker to qml, note that I'm developing a qml plugin and so I can't pass an instance to the qml engine (and I don't want singleton), I have to qmlRegister a class that can then be instantiated from qml.

    Ah, I see, right. My proposed approach is not feasible in this case.



  • Thanks @sierdzio, yes I forgot about implicit sharing ;)

    Actually the documentation mention it also : http://doc.qt.io/qt-5/threads-modules.html#threads-and-implicitly-shared-classes


Log in to reply