3
votes

I'm working with threads in C++/Qt, each one to establish and mantain a QTcpSocket connection.

The problem is that when a disconnection from network comes, those threads must reconnect in a certain order. That's already working, but I have another problem: I need to know into each thread if only current thread has been disconnected, or all of them have been disconnected.

The first idea I had was to pass every thread a reference to a class where I would be able to store each thread status and check everyone else's.

But then I had an easier idea, and I would like to know if it would work or if it impossible: use Qt's slots and signals.

So, every thread I have comes from the same class, but passing different data to the Ctor. So the idea would be to create a slot and a signal inside that class, connect them and emit the signal passing an ID. Then, every thread's slot would be launched... or I may be completely wrong and the only slot launched would be the one from the very same thread which emited the signal.

Is this possible or not?

2

2 Answers

4
votes

First of all, signal-slot connections are done between signals and slots in QObjects, not between threads. Well, a QThread is a QObject, but you really should not derive from a QThread. Do not derive from QThread and you'll be fine.

What you do is:

  1. Get one or more QThreads started (not merely constructed). Those QThreads are just the base Qt classes, don't derive from them. A started raw QThread is blocked, waiting for an event to be posted to its event queue. The default implementation of QThread::run() calls QEventLoop::exec() (or an equivalent). Thus those threads, after starting, don't consume any CPU cycles.

  2. Instantiate a bunch of QObjects. Do all the signal/slot connections as necessary.

  3. Move those objects to one or more QThreads by calling moveToThread(QThread*) on them.

As you see, setting up signal slot connections is done in the usual manner and does not require any special attention. Qt does everything for you, including changing connection types from Qt::DirectConnection to Qt::QueuedConnection when you move the QObjects between threads.

Note that you must not call any methods on those QObjects directly, because you'll be doing so likely from a different thread, so all sorts of bad things will happen as the access is not serialized. You must do any of the below, and only that, in order from fastest to slowest. Note that while #3 is always faster than #4, you need to benchmark between #2 and #3 as it varies.

  1. Post a custom event to the QObject, and handle it in the derived QObject's customEvent(QEvent*) method.

  2. Use the signal-slot connections, and emit signals that got connected to the QObjects' slots.

  3. Use QMetaMethod::invoke on a previously looked up method. The is faster than #4 since the method lookup is done only once in advance.

  4. Use QMetaObject::invokeMethod.

Posting QEvents to QObjects is faster than using signal-slot invocations, because there are no copy constructors called and there's no marshalling done except directly by you upon construction of a QEvent.

Only if profiling/benchmarking shows that event posting is too slow you'd want to look at using QSharedMemory. Having an excessive number of QThreads is counterproductive, you should have probably not more than the number of CPU cores on the system. Using synchronization primitives such as mutexes or semaphores naively requires you to commit way too many threads. You definitely do not want to have one thread per connection! Qt already has an event queue mutex for each QObject, by using the event queue you already use that.

2
votes

Signal and slot connections are done in run time, i.e. you are connecting specific instances of a class. You cannot emit a signal and "broadcast" it to every instance of your class. Signals between different threads is possible, but maintaining them could be a bit of a hassle (It's all explained pretty well in the documentation).

If I were you, I'd probably look into QSemaphore or QSharedMemory.