2

I'm very new to using QThread. I'm using QThread to grab images from an Axis Ip Camera. In the following snippet of the code I'm moving the camera class to a new thread:

QThread camThread;
camera = new AxisNetworkCamera();
camera->moveToThread(&camThread);
camThread.start();
connect(camera, SIGNAL(imageUpdate(QImage)), this, SLOT(upDateImage(QImage)));
connect(camera, SIGNAL(cameraDisconnected()), this, SLOT(cameraDisconnected()));
connect(&camThread, &QThread::finished, &camThread, &QThread::deleteLater);
connect(camera, &AxisNetworkCamera::destroyed, &camThread, &QThread::quit);

I'm invoking the function that starts the camera:

QMetaObject::invokeMethod(camera, "deviceStartup", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(streamUrl)));

The application runs fine and also closes fine when I close it, but what I'm worried is about a couple of warning messages.

First one is when I start the camera:

Type conversion already registered from type QPair<QByteArray,QByteArray> to type QtMetaTypePrivate::QPairVariantInterfaceImpl

2nd one is when I close the application:

QThreadStorage: Thread 0x7fffb8004da0 exited after QThreadStorage 7 destroyed

Should I be worried about these messages? Do they, specially the second 1, mean any memory mismanagement?

Thanks

the_naive
  • 2,936
  • 6
  • 39
  • 68

1 Answers1

2

The code you've posted makes no sense. QThread is not dynamically allocated so you cannot delete it: the deleteLater call will crash. Probably it never gets executed, since you show no code that would stop the thread anyway. There's also no point in destroying the thread after the camera has been destroyed.

The simplest way to do things safely would be to hold the camera and thread by value in your class, and declare them in proper order so that the thread is destroyed before the camera. At that point, the camera becomes threadless and will be safe to destroy in any thread.

There's also a nicer way to invoke methods in remote threads than using invokeMethod:

class Thread : public QThread {
  using QThread::run; // final
public:
  Thread(QObject*parent = 0): QThread(parent) {}
  ~Thread() { quit(); wait(); }
};

// See http://stackoverflow.com/a/21653558/1329652 for details about invoke.
template <typename F> void invoke(QObject * obj, F && fun) {
  if (obj->thread == QThread::currentThread())
    return fun();
  QObject src;
  QObject::connect(&src, &QObject::destroyed, obj, std::forward<F>(fun));
}

class MyObject : public QObject {
  Q_OBJECT
  AxisNetworkCamera camera;
  Thread camThread { this };
  Q_SLOT void updateImage(const QImage &);
  Q_SLOT void cameraDisconnected();
public:
  MyObject(QObject * parent = 0) : QObject(parent)
  {
    connect(&camera, &AxisNetworkCamera::imageUpdate, this, &MyObject::updateImage);
    connect(&camera, &AxisNetworkCamera::cameraDisconnected, this, &MyObject::cameraDisconnected);
    camera.moveToThread(&camThread);
    camThread.start();
  }
  void startCamera(const QString & streamUrl) {
    invoke(&camera, [=]{ camera.deviceStartup(streamUrl); });
  }
};
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • If I understand your `invoke` function correctly, you are using `src` to determine the thread affinity of the caller so the `deleteLater` signal is correctly sent as direct or queued? – jonspaceharper May 27 '16 at 18:28
  • 1
    @JonHarper Close but no dice - yes, it is to do with thread affinity, but it has nothing to do with `deleteLater` nor with the affinity of the caller. It's an idiom to run some code in a different thread. See [this answer](http://stackoverflow.com/a/21653558/1329652) for details. Sending an event with `fun` called in the destructor would have a slightly lower overhead in terms of the number of dynamic allocations done. – Kuba hasn't forgotten Monica May 27 '16 at 18:37
  • Okay. I figured out what it did but wanted to make sure I understood *why*. It's a devilishly clever and concise bit of code. I love it. – jonspaceharper May 27 '16 at 22:37
  • @KubaOber Could you please also show how I can stop the thread when I need to? Thanks. – the_naive May 30 '16 at 12:20
  • @KubaOber What does `[=]{camera.deviceStartup(streamUrl);}` mean here? – the_naive May 30 '16 at 13:06
  • 1
    @the_naive You can invoke `thread.quit()` any time you want, of course. It will exit its event loop and stop the thread. You don't have to, though. You could have the camera object move itself to a null thread, effectively inactivating it. The `[=]{camera.deviceStartup(streamUrl);}` is a C++11 lambda. It is shorthand for instantiating approximately the following functor `class internal { MyObject & obj; QString url; public void operator()() { obj.camera.deviceStartup(url); } internal(MyObject & obj, const QString & url) : obj(obj), url(url) {} };` – Kuba hasn't forgotten Monica May 30 '16 at 16:23