0

I've read lots of articles about why subclassing QThread is a bad idea in most cases and how to use QThread properly, invoking moveToThread method. Here we can see a typical example of such design.

The class I am designing should meet the following requirements:

  • It want to use signals and slots, so I will need an event loop and will use moveToThread.

  • It will expose the interface with signals and slots only. No ordinary C++ methods.

  • All slots should be executed in object's dedicated thread, one thread per object. So the thread should be created as the object is created and should finish when the object dies.

Thus an obvious solution comes to mind (not tested, just a sketch code):

class Worker : public QObject {
Q_OBJECT

public:
    Worker() {
        thread = new QThread();
        // ...Some signal-slot connections may be done here...
        // ...Some other connections may be performed by user code...
        moveToThread(thread);
        thread->start();
    }

    ~Worker() {
        thread->exit();
        thread->wait();
        delete thread;
    }

public slots:
    void process(); // and other interface slots

signals:
    // Interface signals

private:
    QThread* thread;
};

So the point is to declare QThread object as a (private) member of worker class, but I've never ever seen that in any examples or other people's code.

That's why I wonder if this design is flawed? Does it have some fatal drawbacks I didn't notice? Or it's okay, but just not often needed?

Sergey
  • 7,985
  • 4
  • 48
  • 80
  • Firstly, use some kind of RAII object like (unique_ptr etc). Secondly, this is probably better suited for the CodeReview SE site. – OMGtechy Apr 04 '16 at 13:29
  • 1
    @OMGtechy this would likely be closed on Code Review, the author mentioned _"not tested, just a sketch code"_. Code review requires real, working code. See what is [on-topic](http://codereview.stackexchange.com/help/on-topic) there for reference. – Phrancis Apr 04 '16 at 13:36
  • 1
    @Phrancis ah, in which case, I'd suggest having a go first @Sergey! – OMGtechy Apr 04 '16 at 13:37
  • *not tested* - You will learn so much more if you try things yourself and then ask questions on what you don't understand based, on your research. – TheDarkKnight Apr 04 '16 at 13:42
  • @OMGtechy I see that the question is a bit off-topic on this site. Maybe it should be moved to programmers.stackexchange.com? – Sergey Apr 04 '16 at 15:49
  • @Sergey worth keeping in mind that [cross-posting is frowned upon](http://meta.stackexchange.com/tags/cross-posting/info) – gnat Apr 04 '16 at 16:09
  • See also [this question](http://stackoverflow.com/q/32612309/1329652), and [this WIP on a proxy implementation](https://github.com/KubaO/stackoverflown/blob/master/questions/metaproxy-32612309/main.cpp). It might do what you need, transparently proxying calls between threads. – Kuba hasn't forgotten Monica Apr 04 '16 at 16:39

1 Answers1

1

This is possible as long as you move the object out of the worker thread. Here's how you might do it - note that you should hold the thread by value, no point to not using the compiler to manage the memory for you.

class Worker : public QObject {
  Q_OBJECT
  QThread m_thread;
public:
  Worker() {
    m_thread.start();
    moveToThread(&m_thread);
  }
  ~Worker() {
    // Move us out of any thread.
    // moveToThread must always be called from QObject::thread()!
    {
      QObject sig;
      sig.connect(&sig, &QObject::destroyed, this, [this]{
        this->moveToThread(0); // become thread-less
        m_thread->quit();
      });
    }
    // Wait for the thread to stop
    m_thread.wait();
  }
};

Given that work can be done asynchronously via QtConcurrent::run, it's quite possible that you shouldn't be using such an object anyway. Most likely, you'll be wasting threads that are mostly idle, since it's very unlikely that you'll be able to keep the threads runnable always. A non-runnable thread is essentially a wasted resource.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • I suppose this code does nothing: `QThread * curThread = thread(); this->moveToThread(curThread);`. Also I think it's not necessary to do that thread changing tricks in destructor. It does not really matter which thread will destroy the object. – hank Apr 04 '16 at 15:05
  • @hank I suppose that code is necessary because the destructor-calling thread is not guaranteed to be one handled by m_thread. Official docs (http://doc.qt.io/qt-5/threads-qobject.html) say that "Calling delete on a QObject from a thread other than the one that owns the object (or accessing the object in other ways) is unsafe...". – Sergey Apr 04 '16 at 15:46
  • @Sergey "...is unsafe, unless you guarantee that the object isn't processing events at that moment". After you quit and stop the thread, the object does not process any events. – hank Apr 04 '16 at 16:03
  • @hank The object doesn't process any events, but the `object->thread()` is non-zero and different than `QThread::currentThread()`. That indicates that the object's non-thread-safe methods should not be accessed. For a sufficiently complex object, the method presented above ensures that the object will not run in some other thread, for whatever reason. It might be overkill, but it's cheap assurance. – Kuba hasn't forgotten Monica Apr 04 '16 at 16:30