I’m using this connector to RabbitMQ:
https://github.com/fuCtor/QAMQP
I need performance and multi-thread in my application. Please, can I use multi-thread with this connector?
I tried:
void Test::newMessage(QAMQP::Queue * q) {
while (q->hasMessage()) {
QAMQP::MessagePtr message = q->getMessage();
MyEvent *me = new MyEvent();
me->message = message;
poolThreadPosicao->start(me);
}
}
class MyEvent : public QRunnable {
public:
QAMQP::MessagePtr message;
void run() {
s.queue->ack(this->message);
}
};
In some messages the RabbitMQ say: “Unacked 10 messages”. The 10 messages is my qos in broker message. What I need solve this? How to do?
First of all I recommend that you switch to https://github.com/mbroadst/qamqp, as it is the replacement for the original project (which is no longer in active development). The updated code contains many performance and memory enhancements, as well as more complete support for RabbitMQ. Having said that, currently both versions of the project are aimed at having one connection per thread. This means that any of the Channels that you create (an Exchange, or a Queue), will be parented to the connection (Client) which created it and therefore are bound to the creating thread.
One way to handle the problem you are facing would be to inherit from QRunnable and QObject, emitting the message when you have completed your task (NOTE: this is untested, I'm just giving the basic structure):
using namespace QAMQP;
class MessageJob : public QRunnable, public QObject
{
Q_OBJECT
public:
MessageJob(const Message &message)
: m_message(message)
{
}
virtual void run() {
// process the message
// when you are done, emit the finished signal
Q_EMIT finished(m_message);
}
Q_SIGNALS:
void finished(const Message &message);
private:
Message m_message;
};
class Test : public QObject
{
Q_OBJECT
public:
Test(QObject *parent = 0)
: QObject(parent)
{
// setup and connect client
// create queue and start consuming
}
private Q_SLOTS:
void messageReceived(const Message &message)
{
MessageJob *job = new MessageJob; // no parent, this will be autodeleted
connect(job, SIGNAL(finished(Message)), this, SLOT(jobFinished(Message)), Qt::QueuedConnection);
// NOTE: Qt::QueuedConnection is very important as it allows the signal to
// cross threads
QThreadPool::globalInstance()->start(job);
}
void jobFinished(const Message &message) {
m_queue->ack(message);
}
private:
Client m_client;
Queue *m_queue;
};
Related
So, I have this simplified code as an example for my problem:
#include <QDebug>
#include <functional>
void third()
{
qInfo() << "finished everything!";
}
// will use stuff downloaded by `first()`, which will be used by `third`
void second(const std::function<void()>& cb = [] {})
{
// this has to be called after `first()` has been called and finished
// finished, calling callback
cb();
}
// Will download some stuff, which will be used by `second()`
void first(const std::function<void()>& cb = [] {})
{
// finished, calling callback
cb();
}
int main(int argc, char* argv[])
{
first([=] {
second(third);
});
// or, even worse
first([=] {
second([=] {
third();
});
});
}
Is there any way, that I can prevent this callback hell from happening?
I thought about making another thread, where I would call those functions, block that thread and wait for them to finish.
I am not super sure on how I would do that though. So, first of all, is there a better way to write this (maybe even without creating another thread?) and secondly, if I had to make this happen on another thread, how would I do that?
Thanks for any help in advance!
Signals and slots quite naturally clean up this kind of code. For example:
class Downloader : public QObject {
Q_OBJECT
public:
void beginDownload() // this is your first
{
emit downloadFinished({});
}
signals:
void downloadFinished(const QByteArray& data);
};
class DataProcessorA: public QObject {
Q_OBJECT
public:
void processData(const QByteArray& data) // this is your second
{
emit processingFinished({});
}
signals:
void processingFinished(const QDateArray& processedData);
};
class DataProcessorB: public QObject {
Q_OBJECT
public:
void processData2(const QByteArray& data) // this is your third
{
emit processingFinished({});
}
signals:
void processingFinished(const QDateArray& processedData);
};
void myFunction()
{
auto* downloader = new Downloader(parent);
auto* processorA = new DataProcessorA(parent);
auto* processorB = new DataProcessorB(parent);
// make first call second...
QObject::connect(downloader, &Downloader::downloadFinished, processorA, &DataProcessorA::processData);
// make second call third...
QObject::connect(processorA , &DataProcessorA::processingFinished, processorB, &DataProcessorB::processData);
}
This can work even if the download is executed on a separate thread because Qt's signals/slots can work between threads. The data in the signal will be copied to the receiving thread. Ref: https://doc.qt.io/qt-6/qt.html#ConnectionType-enum
There is a bit of boilerplate with all the QObject stuff, but this should allow you to write each piece in isolation and then be able to flexibly connect the individual pieces using QObject::connect.
I am having trouble trying to figure out where this performance degrade in my codes comes from. My program is quite big so I'll simplify using some examples. Say I have a MainUI class subclassed from QMainWindow, which has two member objects, a Worker (subclassed from QThread) and a DisplayPanel (subclassed from QGLWidget). Then a connection is established between a signal from this Worker object (emitted from its run method) and a slot in the DisplayPanel object. And I see performance degrade in the run method if I increase the number of connections.
Basically what I am describing is (very simplified):
class Worker : public QThread {
Q_OBJECT
signals:
void updateDisplay();
protected:
void startWorking() {
// some setup codes
this->start();
}
void run() {
while (1) {
// some processing codes
emit updateDisplay();
}
}
};
class DisplayPanel : public QOpenGLWidget, protected QOpenGLFunctions_5_0_Core {
Q_OBJECT
public slots:
void refresh() {
update();
}
};
class MainUI : public QMainWindow {
Q_OBJECT
public:
explicit MainUI(QWidget *parent = 0) {
// some Ui setup codes
worker = new Worker();
num_panels = 5 // arbitrary number
panels.resize(num_panels);
for (auto p : panels) {
p = new DisplayPanel();
connect(worker, SIGNAL(updateDisplay()), p, SLOT(refresh()));
}
}
void process() {
worker->startWorking();
}
private:
unsigned num_panels;
Worker* worker;
std::vector<DisplayPanel*> panels;
};
Imagine MainUI::process() was called from the GUI.
What confuses me is that there is some noticeable performance degrade in the while loop of Worker::run() if I increase MainUI::num_panels. From my understanding, the increased number of signals-and-slots connections should not affect the performance of Worker very much because the queued requests to DisplayPanel::update() should run on a different thread.
Any insights would be appreciated!
My problem is the following: I need to create class, which contains QStateMachine instance. This class should have slots through which you could "ask" state machine to make transition to another state. And if transition was successful, my class should emit signal about it. How would I implement this? Class should have ability to emit certain signals according to certain slot invoke.
Here is a small example of class:
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = 0)
{
mStateMachine = new QStateMachine(this);
QState *s1 = new QState(mStateMachine);
QState *s2 = new QState(mStateMachine);
QState *s3 = new QState(mStateMachine);
s1->addTransition(); // Transition to s2
s2->addTransition(); // Transition to s3
s3->addTransition(); // Transition to s1
mStateMachine->setInitialState(s1);
mStateMachine->start();
}
signals:
toS1();
toS2();
toS3();
public slots:
slotToS1()
{
/* post event to state machine about
transition to state s1,
if transition was successful,
then emit toS1() signal. */
};
slotToS2(){ /* Similar to slotToS1 */};
slotToS3(){ /* Similar to slotToS1 */};
private:
QStateMachine *mStateMachine;
}
I would be very grateful for your help!
UPD:
The slots are representing defferent kinds of transitions, so that outer class (that will be using MyClass) could 'ask' for some transition. So, the slot send event or signal to state machine, it looks on event or signal and (if in right state) makes this transition. And I want to notify outer class with certain signal, that asked before slot (transition) was made successfuly.
To transition on a slot call, you need to somehow bind the slot to a QAbstractTransition. There are two ways of doing it:
Use a QEventTransition and send a relevant event to trigger it.
Use a QSignalTransition and use an internal signal to trigger it.
To emit signals on state transitions, you can connect the QAbstractTransition::triggered or QState::entered or QState::exited signals to other signals. Remember, in Qt a connection target can be either a slot or a signal.
Thus, using signal transitions:
class MyClass : public QObject
{
Q_OBJECT
QStateMachine machine;
QState s1, s2;
Q_SIGNAL void s_go_s1_s2();
Q_SIGNAL void s_go_s2_s1();
public:
Q_SIGNAL void transitioned_s1_s2();
Q_SIGNAL void transitioned_s2_s1();
Q_SLOT void go_s2_s1() { emit s_go_s2_s1(); }
Q_SLOT void go_s1_s2() { emit s_go_s1_s2(); }
explicit MyClass(QObject *parent = 0) : QObject(parent),
s1(&machine), s2(&machine) {
auto s1_s2 = s1.addTransition(this, SIGNAL(s_go_s1_s2()), &s2);
auto s2_s1 = s2.addTransition(this, SIGNAL(s_go_s2_s1()), &s1);
machine.setInitialState(&s1);
machine.start();
connect(s1_s2, &QAbstractTransition::triggered, this, &MyClass:: transitioned_s1_s2);
connect(s2_s1, &QAbstractTransition::triggered, this, &MyClass:: transitioned_s2_s1);
}
}
Using event transitions is a bit harder, since the events you're using must be cloneable by the state machine. The core module's state machine only knows how to clone the None and Timer events - see its cloneEvent implementation.
The widgets module adds support for various GUI/Widgets events - see the cloneEvent implementation there. You could, in a pinch, use such GUI events for your own purposes - after all, they are sent to a plain QObject that doesn't interpret them in a special way.
You can provide your own cloneEvent implementation that links with the others.
#include <private/qstatemachine_p.h>
class MyClass : public QObject
{
Q_OBJECT
QStateMachine machine;
QState s1, s2;
QEvent e_s1_s2, e_s2_s1;
QEventTransition s1_s2, s2_s1;
public:
Q_SIGNAL void transitioned_s1_s2();
Q_SIGNAL void transitioned_s2_s1();
Q_SLOT void go_s2_s1() { QCoreApplication::sendEvent(this, &e_s2_s1); }
Q_SLOT void go_s1_s2() { QCoreApplication::sendEvent(this, &e_s1_s2); }
explicit MyClass(QObject *parent = 0) : QObject(parent),
s1(&machine), s2(&machine),
e_s1_s2((QEvent::Type)(QEvent::User + 1)),
e_s2_s1((QEvent::Type)(QEvent::User + 2)),
s1_s2(this, e_s1_s2.type()),
s2_s1(this, e_s2_s1.type()) {
s1_s2.setTargetState(&s2);
s2_s1.setTargetState(&s1);
s1.addTransition(&s1_s2);
s2.addTransition(&s2_s1);
machine.setInitialState(&s1);
machine.start();
connect(&s1_s2, &QAbstractTransition::triggered, this, &MyClass::transitioned_s1_s2);
connect(&s2_s1, &QAbstractTransition::triggered, this, &MyClass::transitioned_s2_s1);
}
}
static const QStateMachinePrivate::Handler * last_handler = 0;
static QEvent * cloneEvent(QEvent * e) {
if (e->type() >= QEvent::User && e->type() < QEvent::User+100) {
return new QEvent(e->type());
return last_handler->cloneEvent(e);
}
const QStateMachinePrivate::Handler our_handler = {
cloneEvent
};
void registerHandler() {
last_handler = QStateMachinePrivate::handler;
QStateMachinePrivate::handler = &our_handler;
}
Q_CONSTRUCTOR_FUNCTION(registerHandler())
void unregisterHandler() {
QStateMachinePrivate::handler = last_handler;
}
Q_DESTRUCTOR_FUNCTION(unregisterHandler())
I have had the same problem in the past and I have found the easiest way was to inherit from QState with your own QState class and implement 2 methods called QState::onEntry(QEvent * event) and QState::onExit(QEvent * event).
This way you are able to emit any signal you like when you exit and when you enter a new state.
Here is and example:
file mystate.h
#include <QState>
class MyState : public QState
{
Q_OBJECT
public:
explicit MyState(qint32 stateId, QState * parent = 0);
protected:
void onEntry(QEvent * event);
void onExit(QEvent * event);
signals:
void exit(qint32 stateId);
void enter(qint32 stateId);
private:
qint32 stateId;
};
And file mystate.cpp
#include "mystate.h"
MyState::MyState(qint32 stateId, QState *parent)
{
this->stateId = stateId;
}
void MyState::onEntry(QEvent *event)
{
emit enter(stateId);
}
void MyState::onExit(QEvent *event)
{
emit (exit(stateId));
}
I'm learning Qt and I was reading about Threads, Events and QObjects from Qt wiki, and followed the wiki recommendations on how to handle some work in a while condition but its not working for my specific case. Here's a simple example of what I'm currently trying to achieve.
class FooEvents : public FooWrapper {
public virtual serverTime(..) { std::cout << "Server time event\n"; }
public virtual connected(..) { std::cout << "Connected event\n"; }
}
class Foo : public QObject {
private:
FooAPI *client;
public:
Foo(FooEvents *ev, QObject *parent = 0) : client(new FooApi(ev)) { .. }
private slots:
void processMessages() {
if (state is IDLE)
reqFooAPiServerTime();
select(client->fd()+1, ...);
if (socket is ready for read)
client.onReceive();
}
public:
void connect(...) {
if (connection) {
QObject::connect(&timer, SIGNAL(timeout()), this, SLOT(processMessages()));
timer.start(1000); // I don't get the output from FooEvents
}
}
}
This is a very simple but I think it illustrates my case. Why is this not working and what other alternatives to I have to handle this case? Thanks.s
Edit: The processMessages is being called every second but I don't get any output from the events
Where is timer declared and defined?
If it's local to Foo::connect() it'll be destroyed before it ever has a chance to fire. Presumably it just needs to be a member object of the Foo class.
Also keep in mind that QObject provides it's own simple interface to a timer - just override the protected virtual timerEvent() function and call QObject's startTimer() to start getting those timer events. In this case instead of having a slot to receive the timer events, they will just end up at the overridden timerEvent() function:
protected:
void timerEvent(QTimerEvent *event) {
processMessages();
}
public:
void connect( /* ... */ ) {
// ...
startTimer(1000);
}
This won't work, because processMessages() is not a SLOT.
So Declare processMessages() as a private slot and then try.
You don't declare the timer neither the slot. In the header you must declare:
class ... {
QTimer timer;
...
private slots:
void processMessages();
...
};
Then remember to make the SIGNAL-SLOT connection and configure the timer:
connect(&timer, SIGNAL(timeout()), this, SLOT(processMessages()));
timer.setInterval(1000);
timer.start();
Also timer.start(1000); would be valid...
ANOTHER POSSIBILITY
Other possibility would be to use the timer associated with each Q_OBJECT and overload the timerEvent:
class ... {
Q_OBJECT
...
protected:
void timerEvent(QTimerEvent *event);
...
};
Then you must implement the timer event as this:
void MyClass::timerEvent(QTimerEvent *event) {
processMessages();
}
And you can configure the timer with a simple call to startTimer(1000);
I have 5 classes that interact (maintaining professionally, not author). My problem is that the code that is emitting the signal (there is just one, code below) is never activating the slot (MyWidget::HandleMeasurementChanged). This system has a large degree of complexity. I have tried to reduce that, but think the complexity likely contributes to the problem. There is also a high rate of calls to Observer::notify, but most of these will get filtered out by code that I have not posted here and the Emit calls are fairly rare. If anyone could help point me to why the slot is not getting activated, I'd really appreciate it. It is almost acting like the MyWidget class instance is not processing its event loop. I have had a little success setting the connect type to Direct Connection, but since the emit is in a separate thread and the production code for the slot will update the UI I have ruled that out as a final solution.
class IObserver { public: virtual void notify()=0; };
class ExternalMeasurement { ... };
class Measurement { public: Measurement(ExternalMeasurement source); };
class Observer : public QThread, public IObserver
{
signals:
void MeasurementChanged(boost::shared_ptr<Measurement> measurement);
public:
//called by 3rd party in separate thread
virtual void notify(ExternalMeasurement measurement)
{
_measurement_ =
boost::shared_ptr<Measurement>(new Measurement(measurement));
emit MeasurementChanged(_measurement);
}
private:
boost::shared_ptr<Measurement> _measurement_;
};
class MyWidget : public QWidget
{
private:
Component _component_;
public slots:
void HandleMeasurementChanged(boost::shared_ptr<Measurement> measurement);
public:
MyWidget(Component * component_)
};
MyWidget::MyWidget(Component * component_)
{
_component_ = component_;
connect(
_component_->_observer_,
MeasurementChanged(boost::shared_ptr<Measurement> measurement),
this,
HandleMeasurementChanged(boost::shared_ptr<Measurement> measurement));
}
class Component
{
private:
QApplication * _application_;
MyWidget * _widget_;
Observer * _observer_;
public:
void MainFunc();
}
void Component::MainFunc()
{
_observer_ = new Observer();
...
_application_ = new QApplication(...);
...
_widget_ = new MyWidget(...);
...
_widget_->show();
_application_->exec();
}
This was referenced in the link that Jeremy added in a comment to my question, but just for clarity:
The solution was to add:
qRegisterMetaType<shared_ptr<Measurement> >("shared_ptr<Measurement>");
immediately before the call to connect.